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

虽然项目需求是只展示最新的数据,所以几乎没用什么本地存储.除了通讯录和用户的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[]; CC_MD5(cStr, (CC_LONG)strlen(cStr), result); NSMutableString *hash = [NSMutableString string]; for (int i = ; i < ; 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,只能说本地暂时安全了(后来和一做网络安全聊过,如果目标iPhone没有被破解(越狱),其实存储位置和方式是无所谓的,因为iOS的沙盒机制,应用间是彼此独立的,附keychain导出,http://blog.jobbole.com/58832/).

不过如果WiFi被劫持了,或者用户接入了不安全的网络,通过一些抓包软件,也是很容易造成用户数据的泄露.

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

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

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

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

另外Base64是编码方式,不是加密方式.它是可以被解码的...

上个项目的一些反思 I的更多相关文章

  1. 上个项目的一些反思 II

    上个项目需要使用通讯录,我在回顾自己设计的时候,发现自己少设计了cache这一环. 虽然直接用SQLite在初期体验上没什么大损失,不过可以预想通讯录增长到一定数量后势必会影响体验. 单例模式,全局缓 ...

  2. 上个项目的一些反思 III

    离线缓存 之前的项目因为实时性要求比较高,所以一打开客户端,就开始做网络请求.现在想想,是没有做内容的离线缓存.这个问题,我没意识到.产品经理也没有意识到... 我觉得Archiver,来做比较合适, ...

  3. git上传项目代码到github

    参考: git学习——上传项目代码到github github上传时出现error: src refspec master does not match any解决办法 git 上传本地文件到gith ...

  4. GitHub的用法:到GitHub上部署项目

    先提供两个较好的Git教程: 1. 如何在github部署项目: lhttp://jingyan.baidu.com/article/656db918fbf70ce381249c15.html 2. ...

  5. 如何从eclipse中下载并导入Github上的项目

    eclipse导入项目,方法就是点击File ->Import,选择Existing Projects into Workspace 但前提是,你导入的这个项目原本就是用eclipse的构建的, ...

  6. 参与github上开源项目的大致流程和注意事项

    Foreword github是一个很火的代码托管服务网站,可能好多人都想参与一两个项目玩一玩学习一下,但由于是纯英文的网站,可能又会止步于想法上没有动手实践.接下来我就介绍一下参与github上开源 ...

  7. 在GitHub上管理项目

    在GitHub上管理项目 新建repository 本地目录下,在命令行里新建一个代码仓库(repository) 里面只有一个README.md 命令如下: touch README.md git ...

  8. Eclipse-将svn上的项目转化成相应的项目

    这里假设svn上的项目为maven项目 首先从svn检出项目 其中项目名称code可自己定义更改新的名称 从svn检出的项目结构 然后将项目转化成相关的项目 转换加载中 加载/下载 maven相关内容 ...

  9. 利用gitbash上传项目到github

    GitHub主要是用作基于Git的分布式版本管理系统的库,可以保存和管理自己的代码,而且主要用作代码的合作开发.不过对于我来说,Git控制系统还比较难以掌握,或者开发小系统还不太用得着,因此我把Git ...

随机推荐

  1. php 时间加减

    <?php date_default_timezone_set('PRC'); //默认时区 echo "今天:",date("Y-m-d",time() ...

  2. C# SaveFileDialog的用法(转载)

    #region 保存对话框private void ShowSaveFileDialog(){//string localFilePath, fileNameExt, newFileName, Fil ...

  3. Android View 事件分发机制 源码解析 (上)

    一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个My ...

  4. 开发haproxy管理平台

    1.说明:该脚本仅适用于Linux操作系统2.使用方法: 在该脚本同级目录下要创建一个名字叫做 haproxy 的文件 haproxy 文件内容如下 global log 127.0.0.1 loca ...

  5. 创建.emacs.d目录和.emacs文件

    1.双击bin下的addpm.exe 2.HKEY_CURRENT_USER->Software->GNU->Emacs 新建字符串值HOME,数值数据为emacs的安装路径 3.创 ...

  6. Javascript随记

    一, 理解Javascript的运行环境概念 function say(msg) { alert(this + ' says ' + msg); } var tt = { message: 'Jame ...

  7. SQL0946N错误及DB2事务日志

    在对DB2数据库进行批量增删的时候, 如果数据量比较大会导致SQL0964N错误, DB2 Knowledge center(http://pic.dhe.ibm.com/infocenter/db2 ...

  8. struts2+spring+hibernate(SSH)框架的搭建和总结

    SSH框架:struts2+spring+hibernate,是目前较流行的一种Web应用程序开源集成框架,用于构建灵活.易于扩展的多层Web应用程序. struts2+spring+hibernat ...

  9. 菜鸟初识python request属性及方法说明

    if  request.REQUEST.has_key('键值'): HttpRequest对象的属性 参考: 表 H-1. HttpRequest对象的属性 属性 描述 path 表示提交请求页面完 ...

  10. mysql安装和配置

    一.下载mysql mysql下载页 我用的是5.6,点击旁边的"Looking for previous GA versions?"按钮就能看到5.6版本 mysql-5.6.3 ...