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

虽然项目需求是只展示最新的数据,所以几乎没用什么本地存储.除了通讯录和用户的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. codevs 1015 计算器的改良 2000年NOIP全国联赛普及组

     时间限制: 1 s  空间限制: 128000 KB  题目等级 : 白银 Silver 题目描述 Description NCL是一家专门从事计算器改良与升级的实验室,最近该实验室收到了某公司所委 ...

  2. thrift中的超时(timeout)坑

    最近在项目中采用thrift作为后台服务rpc框架,总体用下来性能还不错,跨语言特性使用起来也还行,但是也遇到了一些坑,其中之一就是超时问题(timeout),如果服务端些的某些业务场景耗时较长,th ...

  3. 创意十足的web布局及交互设计

    富有灵感和创意的设计与一般设计的区别在于,它不那么容易被想到和实现,一旦它被实现,一个非常有趣并且迷人的网站就诞生了. 网站几乎每天都能见到,但是不是每一个网站你都会说“真希望我也能想到过!” 设计者 ...

  4. window.open和window.location.href的几种用法

    windows.open("URL","窗口名称","窗口外观设定"); <A href="javascript:windo ...

  5. VirtualBox装ghost XP

    在win7 professional 64上安装了virtualBox4.3.14 r95030 版本,之所以要安装这个vb,是因为刚升级的vm 打开之后很占用cpu, 网上又说vb不是很占用cpu而 ...

  6. 一行python代码实现树结构

    树结构是一种抽象数据类型,在计算机科学领域有着非常广泛的应用.一颗树可以简单的表示为根, 左子树, 右子树. 而左子树和右子树又可以有自己的子树.这似乎是一种比较复杂的数据结构,那么真的能像我们在标题 ...

  7. sql 关于查询时 出现的 从数据类型 varchar 转换为 numeric 时出错 的解决方法。

    出现这种问题 一般是查询时出现了 varchar 转 numeric 时出了错  或varchar字段运算造成的 解决方法: 让不能转的数不转换就可以了 sql的函数有个isNumeric(参数) 用 ...

  8. Deepin 15.3 编译同步SudaMod 2.0

    1.设置hosts,方便访问国外站点 $ wget https://raw.githubusercontent.com/racaljk/hosts/master/hosts -qO /tmp/host ...

  9. oracle 修改密码

    [oracle@t-ecif03-v-szzb ~]$ sqlplus / as sysdba SQL*Plus: Release 11.2.0.3.0 Production on Mon Nov 1 ...

  10. 解决Spring MVC @ResponseBody返回中文字符串乱码问题

    spring mvc使用的默认处理字符串编码为ISO-8859-1 解决方法: 第一种方法: 对于需要返回字符串的方法添加注解,如下: @RequestMapping(value="/use ...