实现接口签名安全认证
一)需求背景
现在app客户端请求后台服务是非常常用的请求方式,在我们写开放api接口时如何保证数据的安全, 我们先看看有哪些安全性的问题
请求来源(身份)是否合法?
请求参数被篡改?
请求的唯一性(不可复制)
二)采用参数签名的方式验证
为了保证数据在通信时的安全性,我们可以采用参数签名的方式来进行相关验证。
案例:
我们通过给某 [移动端(app)] 写 [后台接口(api)] 的案例进行分析:
客户端: 以下简称app
后台接口:以下简称api
我们通过app查询产品列表这个操作来进行分析:
app中点击查询按钮==》调用api进行查询==》返回查询结果==>显示在app中1)、不进行验证的方式
api查询接口:/getproducts?参数
app调用:http://api.test.com/getproducts?参数1=value1.......
如上,这种方式简单粗暴,通过调用getproducts方法即可获取产品列表信息了,但是 这样的方式会存在很严重的安全性问题, 没有进行任何的验证,大家都可以通过这个方法获取到产品列表,导致产品信息泄露。 那么,如何验证调用者身份呢?如何防止参数被篡改呢?
2)、MD5参数签名的方式
我们对api查询产品接口进行优化:
1.给app客户端分配对应的app_key(key=123456)、secret秘钥
2.Sign签名,调用API 时需要对请求参数进行签名验证,签名方式如下:
a. 按照请求参数名称将所有请求参数按照字母先后顺序排序得到:
keyvaluekeyvalue...keyvalue
如字符串:将arong=1,mrong=2,crong=3 排序为:arong=1, crong=3,mrong=2 然后将参数名和参数值进行拼接;最终得到参数字符串:arong1crong3mrong2。
b. 将secret秘钥加在参数字符串的头部后进行MD5加密 ,加密后的字符串需大写。即得到签名Sign
新api接口代码:
app调用:http://api.test.com/getproducts?key=app_key&sign=BCC7C71CF93F9CDBDB88671B701D8A35&参数1=value1&参数2=value2.......
注:secret秘钥 仅作加密使用, 为了保证数据安全请不要在请求参数中使用。
如上,优化后的请求多了key和sign参数,这样请求的时候就需要合法的key和正确签名sign才可以获取产品数据。
这样就解决了身份验证和防止参数篡改问题,如果请求参数被人拿走,没事,他们永远也拿不到secret,因为secret是不传递的。 再也无法伪造合法的请求。
示例演变:
客户端的算法 要和 我们服务器端的算法是一致的
将参数排序,和secret秘钥进行拼接
“a=1&b=hello&c=world&key=1” 和秘钥(secret=123456)进行拼接
“a=1&b=hello&c=world&123456” =》md5 加密 ===》字符串sign= BCC7C71CF93F9CDBDB88671B701D8A35
最终得到结果
服务器端如何验证?
判断key来验证客户端身份是否合法
判断参数是否被篡改: 服务器也生成一个sign签名,算法和客户端的一致。
流程:
a=2&c=world&b=hello ==》“a=2&b=hello&c=world” ==》secret=123456==》 “a=2&b=hello&c=world&123456” ==》md5 ===》服务器生成的sign ===》如果和客户端传过来的sign一致,就代表合法===》验证参数是否被篡改
3)、不可复制
第二种方案就够了吗?我们会发现,如果我获取了你完整的链接,一直使用你的key和sign和一样的参数不就可以正常获取数据了...-_-!是的,仅仅是如上的优化是不够的
请求的唯一性:
为了防止别人重复使用请求参数问题,我们需要保证请求的唯一性,就是对应请求只能使用一次, 这样就算别人拿走了请求的完整链接也是无效的。
唯一性的实现:
在如上的请求参数中,我们加入时间戳timestamp(yyyyMMddHHmmss),同样,时间戳作为请求参数之一, 也加入sign算法中进行加密。
新的api接口: app调用:http://api.test.com/getproducts?key=app_key&sign=BCC7C71CF93F9CDBDB88671B701D8A35×tamp=201803261407&参数1=value1&参数2=value2.......
示例演变过程
time是客户端发起请求的那一时刻,传过来的
客户端的算法 要和 我们服务器端的算法是一致的
将参数排序,和secret秘钥进行拼接
“a=1&b=hello&c=world&time=201801232”和秘钥(secret=123456)进行拼接
“a=1&b=hello&c=world&time=201801232&123456” =》md5 加密 ===》字符串sign= BCC7C71CF93F9CDBDB88671B701D8A35
判断key来验证客户端身份是否合法
判断参数是否被篡改: 服务器也生成一个sign签名,算法和客户端的一致。
time=客户端在调用这个接口那一刻传的时间 服务器去处理这个接口请求的当前时间 相减,如果这个大于10s;这个链接应该是被人家截取 如果小于10s,表示正常请求
如上,我们通过timestamp时间戳用来验证请求是否过期。这样就算被人拿走完整的请求链接也是无效的。
4)、总结
Sign签名安全性分析: 通过上面的案例,我们可以看出,安全的关键在于参与签名的secret秘钥,整个过程中secret秘钥是不参与通信的, 所以只要保证secret秘钥不泄露,请求就不会被伪造。
上述的Sign签名的方式能够在一定程度上防止信息被篡改和伪造,保障通信的安全,这里使用的是MD5进行加密, 当然实际使用中大家可以根据实际需求进行自定义签名算法,比如:RSA,SHA等。
三、设计方案
配置nginx.conf
编辑access_by_sign.lua
使用java模拟请求
Last updated
Was this helpful?