我如何使用NSSecureCoding来保证收集类的内容



我有一个SGBContainer类的对象,该对象具有名为objects的数组,其中包含SGBObject类的对象。目前,他们各自实施NScododing,但不能实现NSSecureCoding。SGBContainer-initWithCoder:看起来像这样:

- (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的对象而不实例化?

尽管从文档中完全不清楚(听起来像是一个奇怪的语法方法名称),但这就是-decodeObjectOfClasses:forKey:所做的。您会做以下操作:

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

(应到期的地方来信:请参阅nssecurecoding tablecting tablecting fife of Custom类的麻烦)

使用NSSecureCoding没有直接的方法,因为NSCoder不知道。您必须手动消毒数组,以确保它仅包含类型SGBObject的对象(公平地说,击败了NSSecureCoding的目的)。

一种替代方法是自己编码和解码您的数组,而不是依靠NSCoder进行。

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;
}

这是 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 和Unarchive IT读取数据:

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
    }
}

尝试

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

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

相关内容

  • 没有找到相关文章

最新更新