【问题标题】:How to use NSSecureCoding with id objects如何将 NSSecureCoding 与 id 对象一起使用
【发布时间】:2015-09-27 05:29:56
【问题描述】:

我正在创建一个链表并使用容器对对象、下一个和上一个属性进行分组。像 Foundation 集合一样,我希望它实现NSSecureCoding。这是声明:

@interface ListContainer : NSObject <NSCopying, NSSecureCoding>

@property (readonly, nonatomic) id object;
@property (nonatomic) ListContainer * next;
@property (nonatomic) ListContainer * previous;

@end

在实现- initWithCoder: 方法时,我不知道该对象使用什么类:

- (instancetype)initWithCoder:(NSCoder *)aDecoder
{
    self = [super init];

    if (self) {

        _object = [aDecoder decodeObjectOfClass:<#(__unsafe_unretained Class)#> forKey:@"object"];

        BOOL nextIsNil = [aDecoder decodeBoolForKey:@"nextIsNil"];

        if (!nextIsNil) {

            // Decode next
            _next = [aDecoder decodeObjectOfClass:[ListContainer class] forKey:@"next"];

            if (_next == nil) {
                return nil;
            }

            // Link the nodes manually to prevent infinite recursion
            self.next.previous = self;
        }
    }

    return self;
}

我应该改用-decodeObjectForKey: 吗?它仍然是安全编码吗?

【问题讨论】:

  • 当您对对象进行编码时,您可以使用 NSStringFromClass 使用 id 对象的类名来拥有一个私有变量。当你解码它时,用它来解码它。您甚至可以使用键/值对而不是单独的变量在 NSDictionary 中对其进行编码,解码时只需使用 className 键值来实例化适当的类,并使用适当的值来实例化 value 键值。
  • 我确实想到了这一点。但如果我理解正确,当数据来自外部来源(即在线)时,安全编码用于防止实例化未知类型的对象。如果我这样做,我仍然允许攻击者实例化几乎任何对象,因为他们可以在该键中编码他们想要的任何类。他们称之为替代攻击。我希望我的容器受到保护。
  • 您将存储包含指定信息的键/值对的 NSDictionary 将使用安全编码机制存储在磁盘上,这意味着必须对对象类型为 NSDictionary 的知识进行解码在揭示键/值数据的知识之前。
  • 我不能同意这一点。首先,因为不能保证它会留在磁盘上。编码对象通常用于通过网络发送。其次,因为它是NSDictionary 类型的知识是明确写在编码上的。我认为,这将是一个针对攻击的弱解决方案。打破编码就像我自己编码一个列表并检查二进制结果一样简单
  • 忽略之前没有提到通过网络发送此信息的事实以及磁盘上 NSDictionary 将使用安全编码的事实,这意味着没有人能够识别该类是 NSDictionary由于 NSDictionary 是通过以下方式编码的:(其次是因为知道它是 NSDictionary 类型的知识是明确写在编码上的),甚至更进一步,这可能会被自定义容器掩盖。我会注意到还有很多其他方法可以解决这个问题

标签: objective-c nssecurecoding


【解决方案1】:

我最终在 Cocoa 的邮件列表中发布了同样的问题,并且发生了最有趣的讨论。一些亮点:

[...] 用普通的东西制作一个 NSArray,比如 NSString、NSNumber、encode 它,用 decodeObjectForClasses 解码它,没有类。你会 在阵列上失败。将 NSArray 添加到允许的类列表中,然后 .. 有用。所以,你认为, NSArray 会盲目地解码任何东西 它不再安全。

添加一个自定义类的对象 在数组中实现安全编码,它将开始失败 再次。 NSArray 和其他集合类型允许 已知的安全系统类型,如 NSString,但在任何外部都失败 那。 [...]

在这一点上,我明白 NSArray 的行为不像我预期的那样。安全编码似乎不再那么安全了:

这似乎远非理想 [...] 它解码了一组 已知实现 NSSecureCoding 的类是错误的,IMO,对于两个 原因 [...]

1) 包含的类实现了 NSSecureCoding 的事实确实如此 并不意味着我期待它。 [...]

2) 它限制了可以存储的类。 [...]

在替换攻击中获得一个我没想到的类尤其可怕。不过,显然 Cocoa 的承诺是不同的:

[...] 如果您直接使用 NSArray() 或其他集合类 你的编码,你需要检查你得到了什么。他们是 “安全”解码到 Apple 认为解码它们的程度 不会导致缓冲区溢出等,这就是你得到的全部 默认。 [...]

所以,不,NSSecureCoding 不保证容器的安全编码,或者至少不保证类型检查,您必须自己进行。甚至在我最初假设的 Cocoa 的本机数据结构中也没有(有道理,我仍然这么认为)。

道具归罗兰国王所有。你可以看到完整的对话here

【讨论】:

  • 但那是苹果的实现。为什么不自己实现?
  • 当然,这绝对是一个选择。我只是假设NSSecureCoding 会处理这样的情况,毕竟这是在the docs 中相对于NSCoding 写的:This technique is potentially unsafe because by the time you can verify the class type, the object has already been constructed, and if this is part of a collection class, potentially inserted into an object graph。这就是他们引入安全编码的方式,所以我猜它给出了错误的想法。
  • Apple 的实现是合乎逻辑的。 NSArray 是一个通用容器。它不能检查对象类型,因为它与类型无关。如果你实现一个id 对象列表,你会遇到同样的问题。当你打算接受任何东西时,你如何在解码时测试类型?所以你最终会得到类似于[aDecoder decodeObjectOfClass:[NSObject class] forKey:@"next"]; 的东西,这对你的目的来说毫无意义。更重要的是,由于编码数据可以被攻击者操纵,在编码数据中存储对象类信息也是没有意义的。
  • 所以你从“安全编码”中得到的是一个承诺,你得到一个“安全”的数组对象,而不是一个行为不端的对象。
  • 是的,我现在知道了 :) 当然,这是有道理的,但我仍然认为文档具有误导性。我不知道 Apple 是否为此实施了一些解决方法,所以我认为深入挖掘是个好主意。现在我知道了事实,我明白了为什么和为什么不
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-07-12
  • 2021-06-25
  • 2018-02-07
  • 2020-09-24
相关资源
最近更新 更多