Urlencode又称百分号编码,是一种很常用的编码方式,作为前端工程师,少不了与它要打交道。不管是GET请求发送参数,还是POST请求发送body,都少不了要使用Urlencode来编码。
而Urlencode的编码规则又特别简单:取出字符的ASCII码,转成16进制,然后前面加上百分号即可。如果是多字节的字符,则取出每一字节,按照同样的规则进行转换即可。例如问号?
的ASCII码为63
,转换为16进制为3F
,所以%3F
即为?
进行Urlencode编码的结果。

背景
项目需要对外提供HTTP API接口,因此接口鉴权成为一个很重要的内容。为了确保安全,防止中间人篡改数据或进行重放攻击,双方约定的私钥不可以直接出现在请求中,因此采用请求签名的方式来鉴权。
双方约定appKey和appSecret,其中appKey用于识别请求对象,appSecret用于请求签名。具体的方案如下:
- 客户端按照当前时间生成时间戳
timestamp
和随机数nonce
- 客户端按照指定的规则将HTTP请求的queryString和POST的body进行编码,得到一个字符串
data
- 将
timestamp
、nonce
和data
按规则拼接,然后使用appSecret
计算签名
- 将
appKey
、timestamp
、nonce
和签名一起随请求发出
服务端在接到请求后将使用获得的数据和appSecret
重新计算签名,然后判断与客户端给出的签名值是否一致,如果不一致则鉴权不通过。
这一套鉴权机制可以有效防御一些攻击手段:
- 使用了时间戳,可以避免过期请求被重发
- 使用了随机数,可以避免请求被短时间重复发送
- 签名数据包含了完整的时间戳、随机数和请求数据,保证服务端收到的确实是客户端发送的数据,避免被拦截修改
- 签名的密钥是双方协商好的,避免请求伪造
踩坑
在上面的鉴权过程中,一个非常重要的点就是第2点,即将请求的queryString和POST的body进行编码,得到一个字符串。
因为GET和POST请求中,数据都会被Urlencode编码,因此很容易想到,我们也使用Urlencode来进行这个鉴权前的编码过程。
于是坑就这么不期而遇了。