如何设计符合业务的API算法?
结合逆向的一些大型app算法,以及工作中对api改进的一些思考等等…
故有此篇文章,如果你有更完美的方案,可继续沿用您的方案,如果毫无头绪,不知从何下手,
那不妨看看此篇文章,说不定对你有收获。
如何保证安全?如何签名?如何防重?
如何明确调用者的调用行为
方法很简单,那就是把调用行为涉及的关键信息都放到签名内容中进行签名。那么,哪些是关键信息呢?
请求的方法和接口
即每个请求Method和URL,这是每个请求都有的信息,且最为关键的信息。
请求的内容
请求内容一般指HEADERS、QueryString、BODY三大类。
那么,哪一类内容需要添加的签名内容中呢?
一个简单的判断标准,就是看这一块的内容的变更是否影响请求结果,若影响了,一般要求加入到签名内容中;若设计时还不确定,则全部内容加到签名内容中即可。
备注:实际上,一般是哪个字段有影响,添加哪个字段最简洁;但这样的话,服务端就非常麻烦,需要对每个API接口的每个字段分析,无论请求端还是服务端实现都特别麻烦且需要每个接口进行签名联调,不太现实。所以,一般是按大类进行的。
好了,到这里,API签名似乎已经完成了。
防重放
但是,对于部分请求来说,是有请求一次性要求,即同一请求内容一次和两次的结果是不一样的。这种情况下,恶意攻击者,截取一个合法请求后,不停地使用该请求对服务端进行攻击;这种攻击可能造成
(1)如果该请求是写请求,且服务端逻辑允许重复(如A向B转1元),则会造成严重后果。
(2)如果该请求是非常耗时操作,则可能造成服务性能下降。
(3)如果是普通读请求,看似无害,实则量大也是对后端服务性能的一种消化。
所以,在API签名这里,需要进行防重放设计,可以为后端其他服务减少压力。实现的方法,也很简单,那就是调用者每次调用时:
A:调用者生成并带上一个随机数Nonce
B:服务端该随机数是否已出现,有则拒绝,无则存储该随机数并放过请求
这里服务端要保证Nonce唯一,就得存储已经用过的Nonce,但长期保持会带来两个问题
(1)存储成本增加,日积月累,这里要存储的Nonce会越来越多,需要的存储空间就越大
(2)碰撞概率增加,正常服务被拒绝概率增大;这里随着生成Nonce值越来越多,碰撞的概率一定越来越大,若通过增加Nonce值的长度,有增加存储成本。
那么,另一个可行的办法,就是调用者每次请求时带上当前请求时间点Timestamp,然后由服务端限制请求的时效性。
请求的时效性
即某个请求,其请求时间戳Timestamp,和服务端的当前时间在规定时间内(如1分钟内)则为合法请求,反之,则视为无效请求。
如此,上面提到的Nonce值存储成本可能比较大的问题,在结合Timestamp后,可大大降低存储成本,如Timestamp=1min,则仅需存储1min内的请求Nonce值即可,大大减少存储的量级。
版本控制
另外,每个设计都很难做到完美,或者当前看已经比较完善,但随着技术的发展,会逐渐的暴露一些缺陷,此时,想做一个可持续发展的API签名方案,版本迭代自然少不了,所以,请求内容也可加上版本信息。
A计划
最简单的hash算法md5。
例如GET请求:https://url/api/v1/getUserInfo?userid=111
1 | sign=md5("GET/api/v1/getUserInfouserid=111"+timestamp+nonce+salt) |
B计划
有时候,客户端的代码很容易被逆向破解,加密逻辑知道,攻击你的api也就轻轻松松了。
而且不仅仅只有GET请求,post请求中的body,也是信息泄露的重要性。
这个时候可对post请求体做对称加密,设计到对称加密,AES速度最为快速,但key又是个非常麻烦的东西。
有的开发者会选择埋在客户端,当然可以,但是这个是最不推荐的,除非是某个不重要的功能,不然现在逆向工具满天飞,跑两步就trace出来了结果。
这一步就是解决key的存储问题,当然我推荐2套方案。
1.rsa+aes.
这套方案的有点很明显,rsa加密nonce,用nonce作为aes加密的key,一般都是32位。
缺点也很明显,需要在客户端预埋公钥,rsa速度也很慢,当并发量上来时,对服务器的压力很大,尽管有aes加速,但是rsa没有,这种双模,对cpu的利用率往往会大于大多数算法。
2.hash+aes.
用nonce的hash值,作为aes的key,key取定长就行。这种优势很明显,快,同时缺点也很明显,我知道你什么加密不就行了,这部分仅仅作为对抗的开端。
我个人可以思考为:改进下hash算法,切客户端做好so层ollvm混淆和vmp。这套算法基本就能稳定奔放了。
那么post请求体怎么办?
很简单,刚说的这么多只是选择那种算法,有时候post请求体长长的一串,加进签名里面,岂不是浪费传输效率。
一般都是md5(post请求体)=p1.
header:
x-time:时间戳
x-nonce:随机数
x-sign:签名
x-sign= aes(“GET或者POST+querystring+p1+time+nonce”)
但是这种加密的数据,往往只是计算个签名,没有丝毫意义,何必用aes加密呢。
aes加密了body,为啥还要加密签名呢。
正解:有时候业务往往不是这么简单的,比如一些接口会夹杂一些风控进去,会验证用户的环境。才有了C计划
算法类型 | 加密耗时 | 解密耗时 |
---|---|---|
RSA | 380317 ns/op | 34427 ns/op |
AES | 885 ns/op | 938 ns/op |
HmacSHA256 | 1458 ns/op | 1458 ns/op |
C计划
当然还是绕不开aes的问题,因为对称加密,往往能把我们想要的东西给携带过来。
我在逆向某些app的时候,发现他们的请求头都是有好几个加密过的参数,且很长一串。
当解密发现,携带的全是用户的环境,设备环境等等。。
还有时候会发很多日志,基于日志的离线分析,当然这种设计风控层面了。
但往往正是需要这种功能,api签名才会变得这么复杂,不然恶心自己,何必呢。
1.针对有设备指纹的情况,或者强登录的业务,个人建议与账户或者设备绑定一个key。
这个key可以用来aes加密。也就是秘钥下发的功能。当然肯定是密文,需要本地解密拿到真正的key。
2.当然很多情况下,都是随机生成一个key去做对称加密的秘钥,只是看这个key如何去上传了。
我举个例子,假如业务中需要反爬,例如点外卖这种业务来说。
我一部分要防止竞品来爬取我的价格,以至于进行价格调整。还一部分要防止撸羊毛等等,总之需要防御的业务场景太多。
防止ddos等等,不然几个亿的人没外卖吃,哈哈哈。
对于这种强防御的接口,api签名敢随随便便做吗,那肯定不能,都这个级别的app了,在怎么也有人搞吧,而且分不清真假用户,那么他们也许就会上风控,那风控怎么做,光api埋点吗,那也能误杀太多用户了。所以得从设备上下手,因为你点外卖,得从手机点啊。
你要是逆向,你肯定很少人用日常手机吧,光环境就折腾死,肯定选个过时的手机,自己diy一下,或者群控也是,毕竟对抗也是要成本的。那么手机有了,我当然怕你作弊,这个时候设备指纹的作用就体现了。
但是你要说设备指纹绑定了太多东西,有可能我的离线分析引擎,达不到我实时风控的需求,等你分析完,我价格早就爬取完了。所以说实时引擎也很重要,但也误杀多啊。
那不就简单,实时+离线。
我个人理想状态是这样的,也许这个模型需要慢慢优化,我没做过后端风控,不理解。
那么实时检测啥啊,参考wtoken(蚂蚁的app防火墙):
- 首先你的设备指纹要把,一般都是一个id
- 模拟器,hook,云手机,群控,传感器,root,注入调试等等。
往往这些东西,被逆向干净之后,轻而易举就能仿照。
但是,那又怎么样,服务端的策略一大把,人和机器总是能区分的,只不过是误杀率高一点而已。
爬虫总不可能链路来源很完整吧,比如我拿到很多店铺的id,我逆向店铺接口,传一个id就行了。
那么点外卖是怎么点,总有个source——id吧,从哪个页面进来的,把这些参数往上一传,整个模型分析下,
你拿什么和我斗?
很多公司的技术,并没有达到这么恐怖的级别,也可能是我认知不够,可能他们远远做的更离谱。
但是对于这套算法,我的想法是多套结合。
客户端内置好几套算法,api下发版本选择,或者根据设备签发版本号。
且算法强度很大,往往都是一层protobuf。protobuf中包含了许多签名等等。
1 | headers: |
上述一些检测,会在后面慢慢写,总不可能一口气把一头象吞完吧。
一般这种层面都是由网关控制的。
这张图很适合,当然以目前的能力,做对应的模块就行。
风控系统还不完善的话,围绕sdk和api做就行。
参考:
移动安全解决方案:https://www.qianxin.com/trade/detail/tid/29
App防Bot新版ATT方案浅析与算法还原:https://bbs.pediy.com/thread-270914.htm
ppp买菜IOS版设备风控浅析与算法还原:https://bbs.pediy.com/thread-270097.htm#msg_header_h3_4