移动API设计与安全存储
Posted 北漂周
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了移动API设计与安全存储相关的知识,希望对你有一定的参考价值。
最近在重新排查API的时候,我们在企业内部突然讨论到一个问题。我们的APP接口安全吗?一个安全的API接口是该如何设计的?当然,对我看来我们的目前提供给APP使用的API并不安全,自己之前都是在关注逆向与hook也没有思考过类似的问题,如何设计一个安全的API估计很多朋友都遇到过此问题。今天这里总结一下我们内部讨论的结果:
为什么安全性更高的https并没有普及?
说到让我们目前的接口更为的安全,很多朋友第一时间就想到的是将http协议换为https协议哈。我们可以看看所有大型网站,例如京东,淘宝,支付宝,涉及到登录和支付的页面,url都是以https开头,这就意味着,这次通讯是使用https。开放平台的api,例如新浪微博,腾讯等,api请求都是以https开头的。https是业界常用的保证安全性的协议。问题来了,为什么https比http好,在如今网络发展的现在并没有完全普及?
效率低?
涉及安全问题的api,都应该使用https协议。虽然,https为了保证安全性,在效率上是比http协议低。
价格昂贵?
通常来说我们自签名的证书已经能够满足我们的日常需求了,如果不放心ssl服务的供应商有很多,当然国内最有名的就是wosign了,(也许你要问为什么要买国内的, 国内的安全性并没有Symantec高,其实就是因为可以开发票),我们具体查看他们的价格,发现都是在5000~10000不等,其实还算便宜。
https一定安全吗?
这个问题才是我们最关心的问题,首先我们重新理解一下什么是https,不明白的可以先看看我之前的文章:http://blog.csdn.net/yzzst/article/details/46693685。
SSL/TLS协议本身就存在很多漏洞,如SSL 2.0中主要存在的问题如下:
MAC不能覆盖填充长度域,攻击者可能利用这点破坏消息完整性;
缺乏握手认证,攻击者可以篡改密码套件列表,诱骗通信双方使用较弱的密码套件;
使用较弱的或有问题的密码算法(如MD5,RC4等),或者使用不安全的分组模式(如CBC模式);
对于不同的密码学基元使用相同的密钥,违背基本安全常识。
当然,更多的APP是因为对安全常识理解不到位,对信任的证书没有做到排除与过滤,不检查服务器是否可信、过期、CA合法。这样的情况下贸然使用https协议,加上的这层安全也是形同虚设。
竞品的接口设计如何?
如何设计自己的API接口我们同样可以先看看竞品的设计情况,结合来看。抓取了几家竞品获取用户信息的接口
竞品一:
GET http://XXXX.cn/appinterface/App2?a=getUserInfo&loginToken=b5f5b36237e9da4ca2c6a381634407d3&uuid=acc78901-a013-40c9-84a3-c39e42c85af4&faId=69164 HTTP/1.1
User-Agent: Dalvik/1.6.0 (Linux; U; android 4.4.4; MI NOTE LTE MIUI/5.12.17)
Host: XXXX.cn
Connection: Keep-Alive
Accept-Encoding: gzip
完全遵循REST协议,这个api接口通过url参数附带着
参数 | 说明 |
---|---|
a | 具体的action接口名称 |
token | 用户登录后生成的token |
uuid | 设备的唯一标识 |
faId | userid |
竞品二:
POST https://XXXXX.com/api/User/GetUserBaseInfoDetail HTTP/1.1
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.4; MI NOTE LTE MIUI/5.12.17)
Host: XXXXX.com
Connection: Keep-Alive
Accept-Encoding: gzip
Content-Length: 210
"sign":"4E1DEA3C690E6E722DE5DA002006F809","apiVersion":"1.0.0.0","timeStamp":"2016-06-05 21:02:54","userId":100078,"userMobile":"183****7036","channel":2,"version":"2.5.1","appKey":"ycfandroidkzl18d7zdpakfcr"
逆向APP获取数据
- 竞品二所有接口都采用了POST请求,
- 直接就是采用了自签名的的https协议作为app api接口,问题也就是在于它们太依赖与https协议,完全没有理解https就贸然使用,没有做好任何的保护措施。相当于裸奔。
参数 | 说明 |
---|---|
sign | 逆向app发现,sign = MD5(所有参数 + salt) |
apiVersion | 协议的版本号 |
timeStamp | 时间戳 |
userId | 用户id |
userMobile | 用户的手机号 |
channel | 渠道号 |
version | app版本号 |
appKey | 加密的salt (⊙﹏⊙)b 居然这么传 |
当然,这个api接口设计的安全性也显而易见,我们不做过多分析。
竞品三:
GET http://XXXXX.com/app/userIdentity/GetUserNickinfo?uid=869821725&sign=e5e1692a7f6d6e99aef1af367c7689d2&sid=f611ca17c3733c772333b791e6f9c113&platform=3&device_version=4.4.4&peerid=865982027912248&device=MI+NOTE+LTE&cid=m5YT2kTfnwo.&version=3.2 HTTP/1.1
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.4.4; MI NOTE LTE MIUI/5.12.17)
Host: XXXXX.com
Connection: Keep-Alive
Accept-Encoding: gzip
参数 | 说明 |
---|---|
uid | userid |
sign | 所有参数的md5,与竞品二类似 |
sid | session id 登录超时判断 |
platform | 平台 |
device_version | 设备版本号 |
peerid | 一个特殊处理后的时间戳 |
device | 设备名称 |
cid | 客户端标识,一个写死的值 |
version | 协议的版本号 |
如何设计更为安全的API?
看到上面的几家竞品公司的协议,有好有坏,但是同样值得我们的借鉴,我们可以使用Https协议替代我们原有的协议,但是,一些安全的设计与存储的工作同样必不可少。
以目前最为流行的RESTFull协议作为案例,REST的全称是REpresentational State Transfer,表示表述性无状态传输,无需session,所以每次请求都得带上身份认证信息。rest是基于http协议的,也是无状态的。只是一种架构方式,所以它的安全特性都需我们自己实现,没有现成的。
请求身份验证
在HTTP协议之上处理授权有很多方法,如HTTP BASIC Auth,OAuth,HMAC Auth等,其核心思想都是验证某个请求是由一个合法的请求者发起。
Basic Auth
Basic Auth简单点说明就是每次请求API时都提供用户的username和password,这种方式优点和缺点都很明显。如下所示:
GET /private/index.html HTTP/1.0Host: localhostAuthorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
其中的username与password我们可以做一些加密处理(如上面就使用了Base64处理当然可以使用更为复杂的),但是由于每次都传输了重要的数据,违背了最小暴露原则,隐患巨大。
HMAC Auth
每个客户端第一次发出请求时,将自己的唯一HASH MAC 和请求包发给服务器,以后,服务器就依据这个HMAC来判断客户端的唯一性。优劣与Basic Auth类似。
OAuth
由于Basic Auth每次都暴露了重要的数据,那么OAuth的目的就是将此隐患去掉,OAuth(Open Authorization,开放授权)是为用户资源的授权定义了一个安全、开放及简单的标准,第三方无需知道用户的账号及密码,就可获取到用户的授权信息,并且这是安全的。我们所说的OAuth都是只OAuth2.0。
- 用户在服务提供方的网页上输入用户名和密码,然后授权该客户端访问所请求的资源。
- 授权成功后,服务提供方引导用户返回客户端的网页。
- 客户端根据临时令牌从服务提供方那里获取访问令牌。
- 服务提供方根据临时令牌和用户的授权情况授予客户端访问令牌。
- 客户端使用获取的访问令牌访问存放在服务提供方上的受保护的资源。
OAuth是个安全相关的协议,作用在于,使用户授权第三方的应用程序访问用户的web资源,并且不需要向第三方应用程序透露自己的密码。毫无疑问,OAuth对目前来说是一个不错的解决方案,如今微信、qq、百度等第三方授权协议都是基于此,同时OAuth的劣处也同样是在它的思想上,需要资源服务器与授权服务器相互配置,使用与开发上都较为复杂。
授权过滤
既然做到了身份认证,我们就可以对不同来源的token做一个角色划分。常见的如Admin(管理员)、Customer(顾客)。我们简单的书写一个demo,如下所示:
if(isCustom)
if(Pattern.compile("^(/admin).*").matcher(url).matches())
return false;
对于不同角色的用户,开放不同的api接口。
敏感id设计
如以自增长的数据库主键id作为接口协议的唯一查询。
GET http://XXXXX.com/api/User/123
一旦,协议出现问题,极容易被扫库,导致所有的User表中信息暴露。可以自定义一些自己适当的生成唯一id的作为,参考优酷重写url并生成唯一视频id的做法
http://v.youku.com/v_show/id_XMTU5NzA5MDY4NA==.html
操作频率限制
敏感操作必须限制用户操作的频率,可以的话对每分钟操作的次数、每天操作的次数、每个月操作的次数都做限制,防止而已撞库与暴力破解。
if (System.currentTimeMillis() / 1000 - countLimit.getMinuteTime() > 60)
countLimit.setMinuteCount(1);
countLimit.setMinuteTime(System.currentTimeMillis() / 1000);
isLimit = false;
else
if (countLimit.getMinuteCount() >= LOGIN_MIN_COUNT)
isLimit = true;
else
countLimit.setMinuteCount(countLimit.getMinuteCount() + 1);
countLimit.setMinuteTime(System.currentTimeMillis() / 1000);
isLimit = false;
数据格式校验
对于每一个请求过来的参数确保过来的参数合法性,如下是一个简单的测试请求
http://XXX.com/getFile?path=../../../server.config
我们可以对我们需要处理的数据做一个合法性验证,如是否是正整数,是否以…/开头等等。
当然,如果使用的是xml协议,还需要注意下xml注入的问题。
APP加密存储
这个是一个复杂的问题,今天写的太多了,改天再向大家介绍。对于普通的企业来说,如果弄不明白,推荐大家直接使用360加固就好。
ps:好久没有更新博客了,创业这段时间比较忙,这次真的是在大半夜把本篇博客完成。移动API设计上的安全问题远不止于我们想到的这点,本人能想到的也不多,都是大家思维碰撞出来的结果。我们公司比较Open经常会举办一些技术上的考虑与学习会议,有兴趣的朋友可以加一下我的微信,欢迎大家和我交流,欢迎来我公司一些分享技术心得。
/*
- @author zhoushengtao(周圣韬)
- @since 2016年6月6日 凌晨2:45:20
- @weixin stchou_zst
- @blog http://blog.csdn.net/yzzst
/
以上是关于移动API设计与安全存储的主要内容,如果未能解决你的问题,请参考以下文章
SpringCloud Gateway API接口安全设计(加密 签名)
SpringCloud Gateway API接口安全设计(加密 签名)
Spring Cloud 与微服务学习总结(17)—— SpringCloud Gateway API 接口安全设计(加密 签名安全)