When to use NSSecureCoding

2019-03-22 17:20发布

问题:

I'm learning about the NSSecureCoding protocol introduced by Apple in iOS 6.

From my understanding so far, it should be used whenever a class encodes/decodes instances of itself, in order to prevent substitution attacks.

I'm wondering whether it would be appropriate to use it in other cases.

Specifically if a class conforms to NSCoding by encoding/decoding its instance variables, as opposed to the whole instance of itself, would it still be advisable to implement NSSecureCoding?


EDIT

Suppose I have a class that is implementing NSCoding like follows

- (void)encodeWithCoder:(NSCoder *)encoder {
    [encoder encodeObject:self.aString forKey:@"aMeaningfulString"];
}

- (id)initWithCoder:(NSCoder *)decoder {
    if((self = [super init])) {
        self.aString = [decoder decodeObjectForKey:@"aMeaningfulString"];
    }
    return self;
}

and suppose also that no XPC is involved. Instances of this class will be archived in a plist stored on disk.

Security-wise, is there any benefit in using -decodeObjectOfClass:forKey: as opposed to -decodeObjectForKey:?

回答1:

Specifically if a class conforms to NSCoding by encoding/decoding its instance variables, as opposed to the whole instance of itself, would it still be advisable to implement NSSecureCoding?

It depends on the needs of your application. For any old app, persisting things to disk with plain NSCoding is fine, because the information being written, and the application itself, are (should not) be sensitive in nature. But, say you were a bank releasing an application. You may choose to persist some account information, or an API key to disk so you could communicate with your service, verify the identity of the user, etc. You may not want the whole object, but that shouldn't matter. NSCoder doesn't care what is being read, just that it can read it and do its job correctly. This is a problem.

Security-wise, is there any benefit in using -decodeObjectOfClass:forKey: as opposed to -decodeObjectForKey:?

Yes, very much so. The very fact that you're relying on NSCoder to serialize/deserialize an object (let alone the right object) is a huge attack vector. Once a hacker has modified the information in the format used by NSCoder (a plist-like structure, which is both human-readable and very maleable), then there is no way to guarantee that what you're getting back is what you put in. NSCoder doesn't care that someone decided to switch the classes contained in the archive so you're re-constructing an object of a malicious class, and neither does the runtime. In fact, a smart enough hacker would inject a patch into the application to make sure that the deserialized object would induce some sort of undefined state (a stack overflow), which could be used to potentially exploit the entire application.

decodeObjectOfClass:forKey: allows you to force NSCoder to be a lot smarter about deserialization, and it patches what would be a very large hole. That's not to say that you should never use NSCoder without NSSecureCoding, but rather that you have to be smart about what situations you use it in.



回答2:

When you call decodeObjectForKey: to decode an instance variable, the object is still constructed before you can verify its class type (The reason why NSSecureCoding is introduced).

So I assume the same rules are still applied here, and thus it'll better to use decodeObjectOfClass:forKey instead of decodeObjectForKey: when you're decoding instance variables too.



回答3:

Here is my feeling from reading the docs and NSHipsters post.

You use NSCoding for turing your calsses into binary. This is either for arching data to disk, or for interprocess communication. Archiving to disk is relatively safe, however, interprocess communication is risky because you might not trust the source process that is giving you the data.

So first off, if you are just using NSCoding for writing objects persistently to disk, then you do not need to worry about NSSecureCoding (however its quite easy to implement, see below)

Specifically if a class conforms to NSCoding by encoding/decoding its instance variables, as opposed to the whole instance of itself, would it still be advisable to implement NSSecureCoding?

I am not sure how you can decode the whole instance of a class. A class is decoded by decoding some data from the archiver and doing something with it.

- (id) initWithCoder:(NSCoder *)aDecoder 
{
   if (self = [super initWithCoder:aDecoder]) 
   {
       // Old way
       //obj myUnsecureObj = [aDecoder decodeObjectForKey:@"myKey"];

       // New way
       obj mySecureObj = [aDecoder decodeObjectOfClass:[MyClass class]
                                                forKey:@"myKey2"];

       // Use mySecureObj (e.g. save to an property / ivar)
   }
   return self;
}

+ (BOOL)supportsSecureCoding {
   return YES;
}