上个项目的一些反思 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[]; 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的更多相关文章
- 上个项目的一些反思 II
上个项目需要使用通讯录,我在回顾自己设计的时候,发现自己少设计了cache这一环. 虽然直接用SQLite在初期体验上没什么大损失,不过可以预想通讯录增长到一定数量后势必会影响体验. 单例模式,全局缓 ...
- 上个项目的一些反思 III
离线缓存 之前的项目因为实时性要求比较高,所以一打开客户端,就开始做网络请求.现在想想,是没有做内容的离线缓存.这个问题,我没意识到.产品经理也没有意识到... 我觉得Archiver,来做比较合适, ...
- git上传项目代码到github
参考: git学习——上传项目代码到github github上传时出现error: src refspec master does not match any解决办法 git 上传本地文件到gith ...
- GitHub的用法:到GitHub上部署项目
先提供两个较好的Git教程: 1. 如何在github部署项目: lhttp://jingyan.baidu.com/article/656db918fbf70ce381249c15.html 2. ...
- 如何从eclipse中下载并导入Github上的项目
eclipse导入项目,方法就是点击File ->Import,选择Existing Projects into Workspace 但前提是,你导入的这个项目原本就是用eclipse的构建的, ...
- 参与github上开源项目的大致流程和注意事项
Foreword github是一个很火的代码托管服务网站,可能好多人都想参与一两个项目玩一玩学习一下,但由于是纯英文的网站,可能又会止步于想法上没有动手实践.接下来我就介绍一下参与github上开源 ...
- 在GitHub上管理项目
在GitHub上管理项目 新建repository 本地目录下,在命令行里新建一个代码仓库(repository) 里面只有一个README.md 命令如下: touch README.md git ...
- Eclipse-将svn上的项目转化成相应的项目
这里假设svn上的项目为maven项目 首先从svn检出项目 其中项目名称code可自己定义更改新的名称 从svn检出的项目结构 然后将项目转化成相关的项目 转换加载中 加载/下载 maven相关内容 ...
- 利用gitbash上传项目到github
GitHub主要是用作基于Git的分布式版本管理系统的库,可以保存和管理自己的代码,而且主要用作代码的合作开发.不过对于我来说,Git控制系统还比较难以掌握,或者开发小系统还不太用得着,因此我把Git ...
随机推荐
- MySQL 笔记2
-- *************一.数据约束********************---- -- 1.1 默认值 CREATE TABLE student( id INT, NAME ), addr ...
- jquery插件treetable使用
下载后treetable插件后只需要保留jquery.treetable.css样式文件,jquery.treetable.theme.default.css皮肤文件和jquery.treetable ...
- 使用adb shell 进入手机修改文件的权限
1.将android的tools目录加入到path中,或者直接在adb.exe路径下启动cmd窗口2.adb shell 进入手机后,发现是 $ ,不是 # 号3.在进入shell后运行 su ,就可 ...
- 测试工作的疑难杂症bugs
一. 平台:安卓app 代码:重写onresume事件时,没有重新获取sessionId导致记录一下bug重现过程:1.登陆2.退出用户(不退出app)3.重新登录4.home键或者息屏5.再进入ap ...
- Java 基础【13】 文件(文件夹) 创建和删除
使用 java.io.file 创建文件(文件夹),算是 java 最基础的知识,但实战项目中还是需要知晓细节. 比如 File 类中的 mkdir() 和 mkdirs() 的区别. JDK API ...
- elasticsearch suggest 的几种使用-completion 的基本 使用
在lucene里面,suggest 的支持非常完善,可以随心所欲的定制: 但是在es中使用起来就没有那么方便了. es给suggest 分类4类:term :phrase: completion: c ...
- APIPA(Automatic Private IP Addressing,自动专用IP寻址)
APIPA APIPA(Automatic Private IP Addressing,自动专用IP寻址),是一个DHCP故障转移机制.当DHCP服务器出故障时, APIPA在169.254.0.1到 ...
- Java--笔记(7)
61.几种常见排序法的比较 排序法 平均时间 最差情形 稳定度 额外空间 冒泡 O(n2) O(n2) 稳定 O(1) 交换 O(n2) O(n2) 不稳定 O(1) 选择 O ...
- LocalBroadcastManager 的实现原理,Handler还是 Binder?
原文: http://www.trinea.cn/android/localbroadcastmanager-impl/ 对 LocalBroadcastManager 大家应该都不陌生,相对 Bro ...
- JAVA锁的膨胀过程和优化
首先说一下锁的优化策略. 1,自旋锁 自选锁其实就是在拿锁时发现已经有线程拿了锁,自己如果去拿会阻塞自己,这个时候会选择进行一次忙循环尝试.也就是不停循环看是否能等到上个线程自己释放锁.这个问题是基于 ...