安全机制

在调用本平台API的时候, 需要用client_id标明自己的身份, 根据调用方式不同, 有下面两种方法:

A. 首选方案:

通过https调用Api. 因为https的数据在传输过程中会被加密. 无法被窃听, 所以传输的数据是不会泄露的.

因此我们可以放心大胆的在请求中直接传递密钥信息:

client_id应用的Key
client_secretkey对应的secret

形如

http://<platform-hosturl>/api/path/to/method?client_id=xxxx&client_secret=xxxxxx

B. 备选方案

访问非加密的http://地址, 需要对所有参数进行签名. 传递client_id与签名结果.

因为调用过程在技术上存在被窃听的可能, 我们不能直接在数据上传递client_secret. 因此我们需要用一种签名技术. 使得不传递密钥也能证实自己的身份.

client_id应用的Key
sign_methodmd5
sign_time当前时间戳
signSignToken, 下文详解
SignToken = md5(SignString).toUpper()
SignString = Join( [ClientSecret,Method,Path,headers,GetParams,PostParams,ClientSecret], '&' )
  • Method = GET | POST | DELETE | PUT ...
  • Path = /path/to/method
  • headers = urnencode(HeaderKey1 + HeaderValue1 + HeaderKey1 + HeaderValue1 ...)
  • GetParams = urnencode(GetKey1 + GetValue1 + GetKey2 + GetValue2 ...)
  • PostParams = urnencode(PostKey1 + PostValue1 + PostKey2 + PostValue2 ...)
  • ClientSecret = key的Secret密钥

注意事项

  • 其中headers, GetParams, PostParams需要首先对key进行a-z排序.
  • 不是所有的header参数需要签名, 只对X-Api-开头以及Authorization进行签名
  • signtoken是大写

urlencode规则:

RFC 3986 section 2.3

有效字符 = 字母 / 数字 / "-" / "." / "_" / "~"

  • "!" -> %21
  • "*" -> %2A
  • "(" -> %28
  • ")" -> %29
  • " " -> %20

原理解答

问: 为什么要传递sign_time.

答: 那是因为黑客因为无法获知密钥, 虽然无法调用其他API请求. 但是可以用重复发送同样的数据来让系统产生异常. 这个叫做"重放攻击". 通过sign_time, 我们可以判断如果超过时间, 重放就会失去效果.


问: 为什么签名算法要包含请求参数, 只是对secret进行md5不可以么?

答: 那样无法起到任何保护作用, 黑客只需要截取到sign, 就能发起任何接口任何参数的调用. 因此, 为了安全起见, 必须对所有调用的数据加入到签名运算中. 使得改变任何参数, 签名结果都会发生变化. 从而使攻击无法实现.


问: 为什么签名方式如此复杂. 某某平台只对POST请求进行签名即可.

答: 我们的后端Api多样性比较大, 可能有的参数通过header传递, 或者get/post混合传递, 则必须对所有请求进行签名.