上个项目的一些反思 I

最近一直在反思之前的项目,发现了很多问题.比如数据安全...

虽然项目需求是只展示最新的数据,所以几乎没用什么本地存储.除了通讯录和用户的Token.

用户通讯录另表,今天反思下用户的Token的存储,我直接用<Preferences>存在了本地.一旦被非法获取,配合API借口,后果不堪设想...

就像这样.

    /*
     *  正如其名,还是存储些用户的设置比较好~
     */

    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    
    //
    [userDefaults setObject:@"USERSTOKEN" forKey:@"token"];

    [userDefaults synchronize];// return bool value
    
    //
    NSString *Token = [userDefaults stringForKey:@"token"];

 

后来有和后端讨论是不是该用MD5,后端表示不需要阿~(FUCK TJ)

不过转念一下,也对.都能读取到你的token了,加密存储也没什么用,大不了连你加密的一起复制就行...想了想,不如加点salt吧.

//需要向服务器验证token的时候调用
+(NSString *)MD5EncryptWith:(NSString*)code
{
    
    NSString *SaltCode = [NSString stringWithFormat:@"%@%@",code,[self daySalt]];
    
    const char *cStr = [SaltCode UTF8String];
    
    unsigned char result[16];
    
    CC_MD5(cStr, (CC_LONG)strlen(cStr), result);
    
    
    NSMutableString *hash = [NSMutableString string];
    
    for (int i = 0; i < 16; i++){
        
        [hash appendFormat:@"%02X", result[i]];
        
    }
    
    return [hash lowercaseString];
}


+(NSString *)daySalt
{
    NSDate *  senddate=[NSDate date];
    
    NSDateFormatter  *dateformatter=[[NSDateFormatter alloc] init];
    
    //精确到小时,如果通过局域网抓包获得token,能保证一小时后失效,当然也可以精确到分,秒.(需要服务器配合)
    [dateformatter setDateFormat:@"YYYYMMddhh"];
    
    return [dateformatter stringFromDate:senddate];
}

弄完这些我就后悔了.我只要换个存储方式就能解决了..比如keychain..

//
//  KeyChainIO.h
//
//  Created by M on 16/1/13.
//  Copyright © 2016年 Meng. All rights reserved.
//

#import <Foundation/Foundation.h>

@interface KeyChainIO : NSObject


+(void)SaveToken:(NSString *)Token;

+(id)ReadToken;

+(void)DeleteToken;

@end

 

//
//  KeyChainIO.m
//
//  Created by M on 16/1/13.
//  Copyright © 2016年 Meng. All rights reserved.
//

#import "KeyChainIO.h"

@implementation KeyChainIO

static NSString * const KEY_IN_KEYCHAIN = @"com.m1989.info";

static NSString * const KEY_Token = @"com.m1989.token";


+(void)SaveToken:(NSString *)Token
{
    NSMutableDictionary *usernamepasswordKVPairs = [NSMutableDictionary dictionary];
    [usernamepasswordKVPairs setObject:Token forKey:KEY_Token];
    [self save:KEY_IN_KEYCHAIN data:usernamepasswordKVPairs];
}

+(id)ReadToken
{
    NSMutableDictionary *usernamepasswordKVPair = (NSMutableDictionary *)[self load:KEY_IN_KEYCHAIN];
    return [usernamepasswordKVPair objectForKey:KEY_Token];
}

+(void)DeleteToken
{
    [self delete:KEY_IN_KEYCHAIN];
}



#pragma mark ==========================

+ (NSMutableDictionary *)getKeychainQuery:(NSString *)service {
    return [NSMutableDictionary dictionaryWithObjectsAndKeys:
            (__bridge_transfer id)kSecClassGenericPassword,(__bridge_transfer id)kSecClass,
            service, (__bridge_transfer id)kSecAttrService,
            service, (__bridge_transfer id)kSecAttrAccount,
            (__bridge_transfer id)kSecAttrAccessibleAfterFirstUnlock,(__bridge_transfer id)kSecAttrAccessible,
            nil];
}

+ (void)save:(NSString *)service data:(id)data {
    //Get search dictionary
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    //Delete old item before add new item
    SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
    //Add new object to search dictionary(Attention:the data format)
    [keychainQuery setObject:[NSKeyedArchiver archivedDataWithRootObject:data] forKey:(__bridge_transfer id)kSecValueData];
    //Add item to keychain with the search dictionary
    SecItemAdd((__bridge_retained CFDictionaryRef)keychainQuery, NULL);
}

+ (id)load:(NSString *)service {
    id ret = nil;
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    //Configure the search setting
    [keychainQuery setObject:(id)kCFBooleanTrue forKey:(__bridge_transfer id)kSecReturnData];
    [keychainQuery setObject:(__bridge_transfer id)kSecMatchLimitOne forKey:(__bridge_transfer id)kSecMatchLimit];
    CFDataRef keyData = NULL;
    if (SecItemCopyMatching((__bridge_retained CFDictionaryRef)keychainQuery, (CFTypeRef *)&keyData) == noErr) {
        @try {
            ret = [NSKeyedUnarchiver unarchiveObjectWithData:(__bridge_transfer NSData *)keyData];
        } @catch (NSException *e) {
            NSLog(@"Unarchive of %@ failed: %@", service, e);
        } @finally {
        }
    }
    return ret;
}

+ (void)delete:(NSString *)service {
    NSMutableDictionary *keychainQuery = [self getKeychainQuery:service];
    SecItemDelete((__bridge_retained CFDictionaryRef)keychainQuery);
}

@end

 

+(void)SaveToken:(NSString *)Token;

+(id)ReadToken;

+(void)DeleteToken;

能满足需求了.

 

换成keychain,只能说本地暂时安全了.如果接入了不安全的WiFi,照旧会被干.

所以结合MD5再加点salt,客户端发送请求的时候,是自身token结合当前时间进行MD5加密.

服务端做效验的时候,也是取出数据库存储的Token+客服端发送过来的时间进行MD5加密后进行对比

或许能更安全点..或者直接https,只不过有没有过度设计?

其他的,非对称加密RSA,对称加密AES,DES再等着你.

你可能感兴趣的:(上个项目的一些反思 I)