这篇文章一直说写,迟迟没有动手,这两天看到一些应用接口数据被别人爬虫、短信接口被人高频率请求攻击等案列,感觉简单概述分享一下接口安全验证还是有必要的。毕竟当下基本都以客户端应用为主,如果前期疏忽,发布之后的维护升级等将会有很大的麻烦。这里我将主要围绕以下几个方面:
1. 基础的安全策略
2. Restful安全实现方式介绍
3. OSS.Core实现案例
4. OSS.Core接口参数规范
一. 基础的安全策略
这里讨论只针对应用本身,像Https或者防火墙等第三方支持不在此讨论范围。
对于一个接口项目来说,安全策略我个人认为主要分两块:1. 接口验证模块 2. 用户验证模块
1. 接口验证模块
这个模块是对整个接口安全层面负责。对于接口安全而言,特别是客户端接口,是直接暴露在整个互联网中的,我们首先要保证的就是不会被别人冒名请求我们的接口数据。经常使用的就是签名验证,在接口正常的数据传输之外,传递额外的约定加密签名信息等,来过滤非授权的接口请求。
这里可以举一个去年我遇到的典型案列,当时有个外卖的站点短信发送接口没有添加任何验证,接口地址还写在了页面上,可想而知后果有多么严重,网络上的很多短信轰炸机用的就是这些接口,这里给一张当时发现时测试的截图:
当然签名校验只是最基础的安全校验,如果再配合IP限流等校验措施效果更佳!
2. 用户授权验证模块
这个模块主要是对用户个体负责,上一步主要是在一定程度上过滤一些非自身应用接口请求。但是应用本身出了问题,例如漏洞或者被反编译等,又或者是人员流动造成的秘钥泄露,又如何保证接口的真实用户数据不被轻易篡改。
对于这个问题,我们可以独立一个用户验证模块,核心思路就是通过给每个登录用户颁发token(可以通过用户编号加密,或者编号+时间戳),对用户相关的操作通过token验证,用户主键信息不通过接口明文传送,在服务端通过token解密获取。token的加解密过程都在服务端完成,和签名验证模块独立。
这两个模块也可以通过下边的简单时序图了解相关分工:
二. Restful接口下安全实现方式介绍
上边介绍了一个基础的接口验证步骤,这边我以常见的http接口协议来简单介绍下实现过程:
1. 签名验证
这个主要是通过对每次请求通过参数和随机数的排序组合生成唯一的签名【sign】信息,方式多种多样,这里我介绍一种通用做法,如果你对第三方网站签名验证比较熟悉可以跳过。
这里因为一个接口项目可能会给对个应用提供数据服务,我们给每个应用颁发对应的appid和appsecret信息。
第一步,在正常传递的参数外,添加appid,timespan(当前时间戳)参数,把当前参数集合中值不为空的参数按照参数名ASCII码从小到大排序(字典序),使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串str,同时需要注意一下几点。
参数名ASCII码从小到大排序(字典序)
参数的值为空不参与签名
参数名区分大小写
第二步,将得到str使用签名算法,生成sign(主要看和服务器约定的加密算法,一般使用单向签名算法即可),一下是两种常见加密算法
1. 使用MD5
在str后追加 &appsecret=xxx 后再进行md5 加密,即 sign = md5(str&appsecret=xxx)
2. 使用SHA1
因为sha1本身支持加盐操作, 直接使用 appsecret加密 str ,即 sign=sha1(str, appsecret)
第三步: 传递内容 str&sign=xxxx 到服务端。
以上是一个基本的签名实现过程,当然具体的实现可以根据实际情况各自调整,比如签名信息也可以放在请求header中,参数格式也可以是json等,重要的是需要保证:每次请求签名不会相同
2. 用户授权验证实现
这个大家可以参考最近比较流行的 jwt 协议等实现,主要思路是用户授权后,服务端颁发token返回给客户端,在请求相关授权接口时附带token即可。
这里加密算法需要使用双向加密算法,然后服务端在处理请求时,拦截并解密相关信息,保存在上下文中。
三. OSS.Core实现案例
1. 签名验证:
在OSS.Core项目,我把签名相关验证信息放置在请求头(httpheader)中,通过自定义AuthorizeSignMiddleware中间件在程序入口处进行拦截,主要包含以下参数(实现代码详见GitHub):
如果你想了解详细排序加密等处理,可参见OSS.Common中的SysAuthorizeInfo
2. 用户验证模块:
主要通过自定义Attribute注册实现,代码详见 GitHub中AuthorizeMemberAttribute, 同时我会将当前用户信息保存在一个 AsyncLocal 变量中,详细代码参见OSS.Common中的MemberShiper
四. OSS.Core接口参数规范
所有的接口信息中,我会定义套全局错误码,所有的接口返回信息中都会包含ret标识,来返回当前接口的正常与否,比如:如果ret=1423时表示非法应用来源,ret=1425需要去获取授权验证token,当然每个接口也可以自定义其特有的局部错误码。
全局错误码详见:Github 下 ResultTypes
同时,接口请求头中AppSource,AppVersion,AppClient 参数必填,来保证后续的活跃度,用户注册,订单分布等情况统计。