上个项目的一些反思 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 ...
随机推荐
- .Net的内置对象之一 Request
一.Request简介 Request对象是.net的内置对象之一,也是.net中常用的对象,用于获取客户端的信息,可以使用Request对象访问任何基于HTTP请求传递的所有信息.通过Request ...
- codevs 2495 水叮当的舞步
题目链接:水叮当的舞步 我现在开始发题目链接了(主要还是因为懒得整理题面)-- 这道题一开始是看到MashiroSky在写,于是我也开始写这道题了(说白了就是狙击他)-- 这道题看到这么小的范围当然给 ...
- IIS部署站点相关经验总结
IIS部署站点相关经验总结 1.IIS和.net4.0安装是有先后顺序的,应该先安装.net framework 4.0,再安装IIS.如果按相反顺序安装的话,IIS中看不到4.0相关的东西,那么只能 ...
- 关于SIGSEGV错误及处理方法(转)
转自:http://blog.csdn.net/brace/article/details/1102422 今天编程遇到了SIGSEGV错误,比较困惑,所以找了些资料,总结一下: (1)官方说法是 ...
- CentOS7安装MySQL、Redis、RabbitMQ
系统版本 CentOS Linux release 7.2.1511 (Core) MySQL安装 一.下载mysql的repo源 wget http://repo.mysql.com/mysql-c ...
- mysql 加锁测试
今天研究cobar,做执行时间测试,需要对表记录加锁.用了以下两种方式为表记录加锁. 第一种方式: begin; //开始事务 select * from 表名 ( where ……) for ...
- 马后炮之12306抢票工具(四)--抢票Demo,2014年1月9日终结版
时隔一年多,终于朋友的忽悠下吧抢票Demo的最后一步完善了,与2014年1月9日成功生成车票. Demo仅经过自己测试,并未在高峰期进行测试,代码质量很差,因为赶工,套用去年模板并未使用设计模式. 代 ...
- 设计模式——抽象工厂(Abstract Factory)
Abstract Factory 抽象工厂模式(创建型模式): new的问题:实现依赖,不能应变应对“具体实例化类型”的变化. 解决思路:--封装变化点:哪里变化,封装哪里 - - ...
- CSS3幻灯片制作心得
大神勿喷,这是小弟自己学习的痕迹. CSS3动画效果核心代码 @keyframes slideLeft { 0% { left: -500px; } 100% { ; } } @keyframes s ...
- java获取当天,前天,明天,本周,本月,本年的开始日期时间和结束日期时间
package demoone; import java.sql.Timestamp; import java.text.ParseException; import java.text.Simple ...