我有一个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
,则不会应用任何检查。