3

我有一个 class 的对象,SGBContainer它有一个名为的数组objects,其中包含 class 的对象SGBObject。目前,它们都实现了 NSCoding,但没有实现 NSSecureCoding。-initWithCoder:forSGBContainer看起来像这样:

- (id)initWithCoder:(NSCoder *)aCoder
{
    self = [self init];
    if (self)
    {
        _objects = [aCoder decodeObjectForKey:@"objects"];
    }
}

我想改用 NSSecureCoding,据我所知,这意味着将上面的内容更改为:

- (id)initWithCoder:(NSCoder *)aCoder
{
    self = [self init];
    if (self)
    {
        _objects = [aCoder decodeObjectOfClass:[NSArray class] forKey:@"objects"];
    }
}

...这并没有太大的改进,因为无论它们的类如何,数组的内容都将被实例化。如何确保数组只包含类对象SGBObject而不实例化它们?

4

5 回答 5

8

尽管从文档中看不清楚(听起来像是一个奇怪的不合语法的方法名称),但这就是-decodeObjectOfClasses:forKey:它的作用。您将执行以下操作:

NSSet *classes = [NSSet setWithObjects:[NSArray class], [SGBObject class], nil];
_objects = [aCoder decodeObjectOfClasses:classes forKey:@"objects"];

(应得的信用:见NSSecureCoding 自定义类集合的麻烦

于 2015-03-23T05:14:15.357 回答
1

没有直接的方法可以做到这一点NSSecureCoding,因为NSCoder不知道收集。您必须手动清理数组以确保它仅包含类型的对象SGBObject(公平地说,这有点违背了 的目的NSSecureCoding)。

另一种方法是自己编码和解码您的数组,而不是依赖NSCoder这样做。

于 2014-03-13T16:11:50.597 回答
1

Sean D. 的答案是解锁代码是正确的,但有一个警告:基础不保证数组内部的类类型,如果它们是基础类。例如下面的代码演示了这个问题:

@implementation Serializable
- (id)initWithCoder:(NSCoder *)aDecoder {
  return [super init];
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
}
+ (BOOL)supportsSecureCoding {
  return YES;
}
@end

int main(int argc, const char * argv[]) {

  NSArray *foo = @[ @1, @"Gotcha", [Serializable new] ];
  NSMutableData *archive = [NSMutableData data];
  NSKeyedArchiver *archiver =
      [[NSKeyedArchiver alloc] initForWritingWithMutableData:archive];
  archiver.requiresSecureCoding = YES;
  [archiver encodeObject:foo forKey:@"root"];
  [archiver finishEncoding];

  NSKeyedUnarchiver *unarchiver = 
     [[NSKeyedUnarchiver alloc] initForReadingWithData:archive];
  unarchiver.requiresSecureCoding = YES;
  NSSet *classes = [NSSet setWithArray:@[ [NSArray class], [Serializable class] ]];
  // Should throw, but it does not.
  NSArray *loaded = [unarchiver decodeObjectOfClasses:classes forKey:NSKeyedArchiveRootObjectKey];
  if (loaded.count > 2 && ![loaded[0] isKindOfClass:[Serializable class]]) {
    NSLog(@"Successfully performed object substitution attack.");
    NSLog(@"Class: %@", [loaded[0] class]);  // All 3 objects are
    return 1;
  }
  return 0;
}
于 2017-11-15T19:37:57.943 回答
0

尝试

NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:archivedData];
unarchiver.requiresSecureCoding = YES;
MyClass *myClass = [unarchiver decodeObjectOfClass:[MyClass class] forKey:@"root"];

如果您不设置unarchiver.requiresSecureCoding = YES,则不会应用任何检查。

于 2016-01-21T16:44:37.220 回答
0

这是Swift 4.2版本:

假设您有自定义类CustomClass。确保它符合NSSecureCoding协议。

class CustomClass: NSObject, NSCopying, NSSecureCoding {
    var longName: String
    var shortName: String

    init(longName: String, shortName: String) {
        self.longName = longName
        self.shortName = shortName
    }

    // MARK: - NSCoding Protocol

    required convenience init(coder aDecoder: NSCoder) {
        let longName = (aDecoder.decodeObject(forKey: "longName") as? String ?? "")
        let shortName = (aDecoder.decodeObject(forKey: "shortName") as? String ?? "")

        self.init(longName: longName, shortName: shortName)
    }

    func encode(with aCoder: NSCoder) {
        aCoder.encode(self.longName, forKey: "longName")
        aCoder.encode(self.shortName, forKey: "shortName")
    }

    // MARK: - NSSecureCoding Protocol

    static var supportsSecureCoding: Bool = true

    // MARK: - NSCopying Protocol

    func copy(with zone: NSZone? = nil) -> Any {
        return CustomClass(longName: self.longName, shortName: self.shortName)
    }
}

首先,您需要将对象数组转换为二进制数据并将其存档:

let userDefaults = UserDefaults.standard
let customObjectsData = try? NSKeyedArchiver.archivedData(withRootObject: customObjectsArray, requiringSecureCoding: true)
userDefaults.set(customObjectsData, forKey: USER_DEFAULTS_DATA_KEY)
userDefaults.synchronize()

在此之后,您可以从UserDefaults中读取数据并取消归档:

if let customObjectsData = UserDefaults.standard.data(forKey: USER_DEFAULTS_DATA_KEY) {
    if let customObjects = (try? NSKeyedUnarchiver.unarchivedObject(ofClasses: [NSArray.self, CustomObject.self], from: customObjectsData)) as? [CustomObject] {
        // your code
    }
}
于 2018-10-11T11:45:26.443 回答