When to use NSSecureCoding
Asked Answered
E

3

32

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:?

Emirate answered 25/6, 2013 at 15:45 Comment(1)
I dnt know the answer, so +1 for this question.Fusspot
C
30

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.

Crissum answered 26/6, 2013 at 0:58 Comment(8)
very nice answer, thank you very much. Just to complicate things a little bit more, what if I need to support iOS 5 too. I guess checking the class of the returned object is the only way to go, isn't it?Emirate
It's easier to just write your own. I know there are a lot of frameworks now that support key-value-coding to JSON mechanisms, or just redo it in C. Whatever you come up with with undoubtedly be faster and more stable than the mess that is NSCoderCrissum
I'm afraid I don't quite get what you mean by "write your own". Your own coder?Emirate
Yes. There are a million other formats, and a million other ways of serializing objects, I'm sure you can find one that suits your needs and that supports older iOSes (especially if it's in another language).Crissum
The problem with checking the class is that even among Foundation objects, the isa pointer can lie to you. The very fact that object_setClass() and isa assignment exists means you're powerless to stop any old object from slipping by undetected.Crissum
Thank you for the suggestion. Unfortunately, in this specific scenario, I'm just an auditor, not a developer of the product. I frankly don't think that the developer team will go through such a massive change in the architecture. Would checking the class still be a reasonable "patch" for iOS 5?Emirate
"Reasonable," but by no means perfect.Crissum
@Crissum Are you aware if Encodable guards against object substitution attacks in the same way as NSSecureCoding?Orcus
C
0

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.

Clatter answered 25/6, 2013 at 16:40 Comment(2)
thank you for the answer. I added an example to my question, that you may want to take a look at.Emirate
In most of the apps, where you're the one who encodes and decodes the data, there is no real benefit from calling decodeObjectOfClass:forKey: instead of decodeObjectForKey:.Clatter
A
0

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;
}
Algia answered 25/6, 2013 at 16:51 Comment(3)
so I guess my question now is: provided that everything is archived on disk and no XPC is involved, is it worth to convert an existing code base to adopt NSSecureCoding? Does it have any advantage?Emirate
My feeling is No - I suspect apple use it internally for XPC. I.e. if ([class conformsToProtocol:@protocol(NSSecureCoding)] && [class supportsSecureCoding]) useWithXPC else throwErrorAlgia
Ok, for the sake of completeness, I posted an example in my edited answer. Thank you for the answerEmirate

© 2022 - 2024 — McMap. All rights reserved.