Nsuserdefaults is an iOS commonly used data persistence method, easy to operate, with nscoding and Nskeyedarchiver, it is easy to convert the data model into NSData directly stored in nsuserdefaults. What about the security of data by using Nsuserdefaults to save data?
The essence of nsuserdefaults is to use plist to store data and write the data stored in Nsuserdefaults to a plist in bundle identifier.
The following is a simple example.
@interface testinfo : nsobject<nscoding>@property(nonatomic, retain)NSString*username;@property(nonatomic, retain)NSString*phone;@property(nonatomic, retain)NSString*ticket;@property(nonatomic, retain)NSString*email;@property(nonatomic, retain)NSString*passport;//Confusion@property(nonatomic, retain)NSString*UIOFDSAOUISHJ;@property(nonatomic,Assign)BOOLISLGBT;@property(nonatomic, retain)NSNumber*age;-(void) Savelogininfo;@end @implementation testinfo -(ID) Initwithcoder: (Nscoder *) adecoder{if( Self=[ SelfInit]) { Self. Username=[adecoder decodeobjectforkey:@"username"]; Self. Uiofdsaouishj=[adecoder decodeobjectforkey:@"Uiofdsaouishj"]; Self. Phone= [Adecoder decodeobjectforkey:@"Phone"]; Self. Email= [Adecoder decodeobjectforkey:@"Email"]; Self. Passport= [Adecoder decodeobjectforkey:@"Passport"]; Self. Ticket= [Adecoder decodeobjectforkey:@"Ticket"]; Self. ISLGBT= [[Adecoder decodeobjectforkey:@"Passport"] Boolvalue]; Self. Age= [Adecoder decodeobjectforkey:@"Ticket"]; }return Self;} -(void) Encodewithcoder: (Nscoder *) acoder{[Acoder encodeobject:_username forkey:@"username"]; [Acoder encodeobject:_uiofdsaouishj forkey:@"Uiofdsaouishj"]; [Acoder Encodeobject:_phone forkey:@"Phone"]; [Acoder Encodeobject:_email forkey:@"Email"]; [Acoder Encodeobject:_passport forkey:@"Passport"]; [Acoder Encodeobject:_ticket forkey:@"Ticket"]; [Acoder encodeobject:[NSNumberNUMBERWITHBOOL:_ISLGBT] forkey:@"ISLGBT"]; [Acoder encodeobject:_age forkey:@"Age"];} -(void) savelogininfo{Nsuserdefaults*cache=[NsuserdefaultsStandarduserdefaults];NSString*GAMEKEYSTR = [NSStringstringwithformat:@"Cc_userinfos"]; NSData *logininfo=[nskeyedarchiver Archiveddatawithrootobject: Self];Nsmutablearray*array = [[NsmutablearrayALLOC] init]; [Array Addobject:logininfo]; [Cache Setobject:array forkey:gamekeystr];}@end
When you open plist, you can see that the stored data is saved according to the type of data written by Nsuserdefaults.
The data stored in the example code is used in Savelogininfo, as shown below. Looking at the binary data stored in plist, is it easy to break the data?
<62706c69 73743030 d4010203 04050623 24582476 65727369 6f6e5824
6f626a65 63747359 24617263 68697665 72542474 6f701200 0186a0ab
07080f0d 0910110a 1b1c1d55 246e756c 6cd9090a 0b0c0d0e 0f101112
13141516 171 8191a 5570686f 6e655674 69636b65 74566973 4c474254
5624636c 6173735d 75696f66 6473616f 75695348 4a536167 65587573
65726e61 6d655565 6d61696c 58706173 73706f72 74800480 07800880
0a800380 09800280 05800609 1017d21e 1f20215a 24636c6 1 73736e61
6d655824 636c6173 73657358 54657374 496e666f a2202258 4e534f62
6a656374 5f100f4e 534b6579 65644172 63 686976 6572d125 2654726f
6f748001 00080011 001a0023 002d0032 00370043 0049005c 00620069
00700077 00850089 009200 98 00A100A3 00a500a7 00a900ab 00AD00AF
00b100b3 00b400b6 00bb00c6 00cf00d8 00db00e4 00f600f9 00fe0000
00000000 0 2010000 00000000 00270000 00000000 00000000 00000000 0100>
According to Encodewithcoder, NSObject used nscoder for encode. It is reasonable to say that, without knowing the class parameters, it is impossible to recover the original encrypted data from this piece of binary data.
But what about the actual operation? Assuming that you only get this plist file, do not know the TestInfo case. for progressive analysis.
nsstring *infopath = [[ nsbundle Mainbundle] pathforresource:@ "Info" Oftype:@ "plist" ]; nsmutabledictionary *data = [[nsmutabledictionary alloc] Initwithcontentsoffile:infopath]; nsarray *cachearray=[data objectforkey:@ "CC_ Userinfos "]; if (Cachearray) {for (int i=0 ; I<cachearray.count ; i++) {NSData *data=[cachearray objectatindex:i]; nsstring *logininfo=[nskeyedunarchiver Unarchiveobjectwithdata:data]; } }
First copy the plist data into the info.plist, read into the memory into NSData, because do not know what type of data from the conversion, first use NSString as the data type.
NSString *logininfo=[NSKeyedUnarchiver unarchiveObjectWithData:data];
Does not mean, because the type does not match, error, but from the error message, you can see the original type of data is testinfo.
.- to- to -: Geneva:03.260pannavicontrollerdemo[48761:1143922] * * * * terminating app Due toUncaught exception ' nsinvalidunarchiveoperationexception ', Reason: ' * * *-[nskeyedunarchiver Decodeobjectforkey:]: Cannot decode object of class(TestInfo) forKey (Root); the classmay be definedinchSource codeorA library that is notLinked
What happens when you encode this data as nsstring?
NSString *str =[[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]; NSLog(@"%@",str);
.- to- to -: One:05.130nsuserdefaultsdatasavedemo[48898:1151203] bplist00?& ' T$topX$objectsX$versionY$archiver? Troot€? U$nullùxpassportvticketuphonesage]uiofdsaouishjv$classUemailVisLGBTXusername
Because of the characteristics of OC language, the parameter naming on the readability, so in this section of the nssting can probably guess TestInfo several attributes (capital separation), such as passport, ticket, phone, age, email, username.
[Uiofdsaouishj, ISLGBT] is a two-name non-canonical property that I intentionally wrote.
Presumably after guessing the name of the property, Initwithcoder the method again and get the TestInfo clear text.
The above tests were done because of doubts about the Nsuserdefaults's preservation of the data. It is proved that the nsuserdefaults data preservation is poor, and before using encodeobject, it needs to encrypt with its own encryption algorithm, even if the class is analyzed, the plaintext data will not be read.
Example code
Nsuserdefaults Data Preservation security [Black tech analysis]