我有一个类SGBContainer
的对象,它有一个名为objects
的数组,其中包含类SGBObject
的对象。目前,它们各自实现NSCoding但不实现NSSecureCoding。 -initWithCoder:
的{{1}}如下所示:
SGBContainer
我想切换到使用NSSecureCoding,据我所知,这意味着将上述内容更改为:
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [self init];
if (self)
{
_objects = [aCoder decodeObjectForKey:@"objects"];
}
}
...这不是一个很大的改进,因为数组的内容将被实例化,无论他们的类。如何确保数组只包含类- (id)initWithCoder:(NSCoder *)aCoder
{
self = [self init];
if (self)
{
_objects = [aCoder decodeObjectOfClass:[NSArray class] forKey:@"objects"];
}
}
的对象而不实例化它们?
答案 0 :(得分:4)
虽然从文档中看不清楚(并且听起来像是一个奇怪的不合语法的方法名称),但-decodeObjectOfClasses:forKey:
就是这样做的。您可以执行以下操作:
NSSet *classes = [NSSet setWithObjects:[NSArray class], [SGBObject class], nil];
_objects = [aCoder decodeObjectOfClasses:classes forKey:@"objects"];
(信用到期:见NSSecureCoding trouble with collections of custom class)
答案 1 :(得分: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;
}
答案 2 :(得分:0)
使用NSSecureCoding
没有直接的方法,因为NSCoder
不能识别集合。您必须手动清理数组,以确保它只包含SGBObject
类型的对象(公平地说,这有点违背了{{1}}的目的。)
另一种方法是自己对数组进行编码和解码,而不是依靠NSSecureCoding
这样做。
答案 3 :(得分:0)
尝试
NSKeyedUnarchiver *unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:archivedData];
unarchiver.requiresSecureCoding = YES;
MyClass *myClass = [unarchiver decodeObjectOfClass:[MyClass class] forKey:@"root"];
如果您未设置unarchiver.requiresSecureCoding = YES,则不会应用任何检查。
答案 4 :(得分: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
}
}