以下内容为自己的一些经历和一些查阅资料的总结,希望能帮助到需要的人
一 iOS上的加密思路
问题
这两天一直在想公司app的安全问题,因为发现很多app登录的时候其实是裸奔的,带着账号和密码,等塞进post 请求参数里,直接飞向了服务器。这本都是敏感信息,不应该这样轻易暴露在外边。以前所在公司,也吃过这种亏。之前也一直看过加密的问题,但是也没有深入的理解,这几天查了一下资料,所以稍微记录下使用的流程。以后真的用到这个过程时候,掉坑了再回头填。
过程
1、客户端将 username 和 pwd 做md5 处理,然后再加上客户端的AES key,将这部分内容通过RSA加密,传给服务器。
2、服务器会通过一个私匙,解开登录传过来的数据,获得 username、pwd,和 客户端的AES key。
3、登录成功后,服务器也回生成服务器端的AES key,然后将登录成功的状态 和服务器端生成的AES key,通过客户端的AES key进行加密,回传给客户端,完成这次登录的请求。
4、客户端收到登录回调的数据,通过客户端自己的AES key 将数据解开,获得登录的状态和服务器端的AES key。获取了服务器端的AES key 之后,自己本地生成的AES key丢弃,并保存服务器端的AES key。
5、以后需要加密的信息就直接通过服务器端的 AES key 加密,传给服务器。
6、服务器可以动态控制AES key,这样伪造请求被发现后,也可以更换服务器的AES key。
流程图
RSA、AES加密处理流程一些Tips
1、RSA只在登录的时候使用。为什么只在登录时候使用,因为RSA加密有一些限制,首先他的密文长度只有117个字节,如果你需要加密的内容过长,那么可定会超过117,这样就超出了限制,无法完成进一步的处理。其次,RAS加密是需要时间的啊,骚年!普通内容,每次使用RAS加密,效率肯定不高啊。
2、在解编码过程中,涉及到了base64,记住base64 超过一定长度会自动换行,拿到的base64 编码,记得去掉换行符,不然是解不出来的。
3、用服务器的AES key原因就是可以动态的改变,如果一直使用客户端的AES key,一旦泄露之后,如果要修改密匙就只能APP升级了。
二 iOS安全开发防护摘要
前段时间集中时间写了一本《iOS应用逆向工程—分析与实战》,马上就要上市。我想大概会有一部分iOS程序员觉得:你所说的这种越狱平台的技术压根上不了AppStore,与我何干?不过我相信另一部分人肯定会有相当大的触动的。
写作这本书的时候,所有目标都是集中在进攻方向上,而没有太多考虑防守。因为从逆向工程的角度,确实是非常不好守的:微信、QQ那样的聊天工具,聊天记录可以截取;支付宝、网银那样的支付工具,用户信息和密码可以截取;至于其它更多app,放到逆向工程的面前感觉就是一个在裸奔的小孩,哦不,一群裸奔的小孩。
虽然知识从来都是矛与盾的关系,但对上了逆向工程,确实就易攻难守,不过,尽管是难守,总还是可以守一守的。下面从笔者的经验上来分别罗列一下遇到的问题,及某些app的实际对策。
1、本地数据
很多开发者喜欢用NSUserDefaults来存储一些全局数据,但是这些信息可以直接在app目录文件中读到,如果你也把username和password存在里面。。。当然,如果安全级别不高的程序,这么写也没什么问题;
很多app也会存一些简单的数据到本地sqlite文件中,但实际上,完全可以使用加密sqlite的,当然,还是那句话,安全级别不高的程序,这么写没什么问题。
2、网络接口保护
现在绝大部分的app都已经是联网程序了,经典模式就是http+json,客户端一解析,再做的漂亮一点就齐活。然而只要架上Charles或是tcpdump,完整的url request+response就看的清清楚楚。有了这些url,你的网络数据就无密可保,尤其是一些资源型的服务,直接通过getList的模式就能把所有的数据抓回来。
这种情况下,即使是https也没用,Charles轻松就能搞定,可以看相关的文章。而且即使不用Charles或tcpdump,直接用代码hook网络接口,一样能把数据拿到。
另外插播一句:json是个好东西,它的展现就是一个Dictionary,数据呈现的非常清晰。但是它也绝对是最容易被盯上的点。设想一下,攻击者hook了接口数据,此时直接把json或Dictionary打印出来,信息是不是就一览无余了?在笔者的概念里,把Dictionary/json转成普通的业务对象再传递,即使被拦截,起码不会那么直观地被看到。当然了,这只能给逆向者增加一点麻烦,纯属细枝末节。
笔者看过不少app,大概70%都是这种全裸奔的网络请求,比如“网易新闻”;剩下的会对url做一定的保护,比如加token和timestamp,如“36kr”,这样确实就把抓数据的难度陡然提升了;最“变态”的是“我查查”,他们直接把url做了加密/编码,这种情况下想要复制出来就需要大费周章,而且如果这种编码方式不是通用的,就更难逆向解析了。
3、混淆
混淆有两种模式,一种是对函数名做混淆,另一种是对代码做混淆,两种都直接增加逆向的难度,但是挡是挡不住的,尤其是逆向者还有大把的时间。
4、程序结构
在app这种小而美的领域中,如果代码结构也设计的非常简洁漂亮,实在是一件令人赏心悦目的事情。不过对于逆向工程,越是这种设计结构,越容易定位,有时甚至不用上什么大招,直接从.h文件名和函数列表就能初步定位想要关心的核心代码,如WhatsApp。
而一个反例是微信,微信的质量水准是有目共睹的,不过想要对它进行逆向工程就是一件比较痛苦的事情,因为最新的5.1有3000多个.h文件,如此庞大的代码量直接将逆向工作量提升一个数量级。在笔者的分析过程中,经常会被一堆的Mgr、Event、Service给搅的心烦意乱。
这2个app只是两个极端的比较,毕竟WhatsApp全公司才50人,一个iPhone版的开发者不过数人,结构自然清晰;而微信早已是一个庞然大物,各部门之间相互合作,一层层的代码库抽象封装好给其它组调用,即使是很简单的调用可能也要串3、4个场,不过这样也间接增加了逆向的复杂度。
5、C或block函数
现在iOS方面的逆向工具都是针对OC来的,而一旦代码以C函数或block函数来实现,相当于就采用了匿名函数,想要定位它就只能使用IDA,并且还很容易定位不准。笔者曾经在查看知乎日报的一段对token进行编码的问题上绕了很大的圈子,依然没有定准位,最后干脆放弃了,那段代码就是拿block来完成的。而且,如果是C代码,在IDA中看起来就不是易读的OC风格伪代码了,看起来也会比较累。
总的来说,还是要看开发者比较在意哪方面的安全,才好考虑如何防护:
1、想要保证程序中的信息不被截取
几乎是不可能的,因为即使再加密,密文也总要变成明文供业务逻辑来使用,攻击者只要盯住这个点就能达到目标;
2、想保护服务器的资源
客户端在相当程度上只是一个UI展现,即使被破解也不是那么重要的,真正重要的是服务器端的资源,那么就要在网络层进行防护了。现在大家都比较喜欢用http,而且都集中喜欢使用AF、ASI等几个开源库,逆向者只要盯住这几个口,就能把来往的request+response全部获取,进而可以仿制一个客户端,此时服务器的资源也就全面开放了。同样的,使用AsyncSocket也是一回事。
此时有3种防护措施:
1)服务器控制,不允许同一IP/客户端频繁抓取数据;
2)对url加上token、timestamp,甚至url编码,虽然碰上高手还是不能100%安全,但是破解者想要达到目的,就需要考虑付出的精力是不是值得了;
3)传输数据加密,并且将加解密的代码层级加深,尤其是放到C或block函数中,这样又将安全等级提高了一级。
3、想保护程序不被伪造或克隆
克隆/伪造其实是件非常容易的事,甚至都不需要太多逆向工作,只要能把网络请求搞定,剩下的就是代码量、以及是否比被仿者做的更好的问题了。
总的来说,客户端没太多可保护的空间,它应该就是个业务展现和数据解析的工具,与其花精力想防这个防那个,不如把精力放在网络接口的保护上,尤其是加解密代码提取到C或block中。
以上只是笔者个人的一些经验,希望大家能多多补充。
附:微信的安全防护措施,笔者认为是比较值得学习的,他们的步骤是:
1、不在业务表现上做太多无用功
除了代码结构比较庞大之外,微信没有做特别的动作;
2、服务器控制
所有的决断性的动作由服务器控制,几乎不可能靠通过篡改客户端的代码来获取更大的权限。笔者曾经针对每天20个漂流瓶的限制尝试扩大权限,但是即使把本地代码逻辑放开,在超过20个瓶子之后,再捞回来的永远是海星,显然是服务器控制住了。
当然,本地LBS坐标是无法被服务器控制的,这也就是坐标穿越插件能起作用的根本原因。
3、网络数据加密
通过对网络接口的拦截,虽然能得到socket收取的二进制数据,首先是无法直接解密,其次是从那许多代码中找解密代码非常费劲,纵然加解密都能搞定,想伪造也十分麻烦,packet中包罗了相当多的信息,我估计就是把他们的代码开放给开发者,想理清这一部分都不是一件轻松的事情。
4、登录唯一性
即使能够伪造微信的请求,一旦登录获取数据,原账号就会被踢下线并且得到通知,这本身就是一个安全警告,说起来也算是服务器控制。
如果开发者对于安全性的设计都能达到微信这一级别,安全级别就是比较值得信赖的了,但是这也不表示就解不出来,只要逆向者的水平达到熟练程度以上(自然是越高越好),并且愿意投入大量精力在某个他们认为值得的app上,几乎都能攻的下来。