本周的简短文章:你需要了解的关于 NSSecureCoding
的一切。
NSSecureCoding
是在 iOS 6 / OS X Mountain Lion SDKs 里推出的协议。除了少数在 WWDC 中有提到,NSSecureCoding
却依然相对模糊,大多数开发者都可能听说过它,但也许永远没有走近看看它到底做什么。
NSSecureCoding
通过加入类方法 supportsSecureCoding
来扩展了 NSCoding
协议:
如果一个类符合 NSSecureCoding
协议并在 + supportsSecureCoding
返回 YES
,就声明了它可以处理本身实例的编码解码方式,以防止替换攻击。
具体来说,符合 NSSecureCoding
协议并重写了 -initWithCoder
的类应该使用 -decodeObjectOfClass:forKey:
而不是 -decodeObjectForKey:
。
为什么这很重要?回想一下,NSCoding
是基础类库中将对象归档到文件系统上,或复制到另一个地址空间的一种方式。如果用 -decodeObjectForKey:
用于把对象解码成实际的对象,就不能保证创建的对象是预期的结果。如果该表示已损坏 - 具体地说,在改变目标类(并且因此指定初始化方法)的时候 - 应用程序将会面临构造未知对象的风险。无论是恶意或偶然的编码错误,这都可能会导致严重的问题。
这不是一个苹果跟另一个苹果的比较,而有点类似于最近在 Rails 中发现的 YAML 漏洞。
对于一个 XPC service 来说,它的目的是为安全考虑的,因此数据的完整性显得尤为重要。很可能 XPC 会对后续的 iOS 和 OS X 版本增加影响,所以最好把这一切都铭记在心。
总之,NSSecureCoding
通过建立关系给补上这个漏洞做了最佳实践。现在,要对一个对象进行解码,需要该类提前被声明。
而一个标准的,安全的 -initWithCoder:
实现可能需要一个检查,比如:
if let object = decoder.decodeObjectForKey("key") as? SomeClass {
// ...
}
id obj = [decoder decodeObjectForKey:@"myKey"];
if (![obj isKindOfClass:[MyClass class]]) {
// fail
}
…一个符合 NSSecureCoding
协议的类应该使用:
let object = decoder.decodeObjectOfClass(SomeClass.self, forKey: "key") as SomeClass
id obj = [decoder decodeObjectOfClass:[MyClass class]
forKey:@"myKey"];
有时候,一点点 API 的变化,会使得所有事情都千差万别。
所以,现在你知道什么是 NSSecureCoding
了。也许不是今天,也许也不是明天,但总有一天,你可能需要实现 NSSecureCoding
。而当那一天到来的时候,你会做好准备。
保重,各位。
---
除非<a href="https://nshipster.cn/" target=_blank>另有声明</a>,本文采用知识共享「<a href="https://creativecommons.org/licenses/by-nc/3.0/cn/" target=_blank>署名-非商业性使用 3.0 中国大陆</a>」许可协议授权。