API 签名机制一篇讲透:md5 签名 + mall_id + tm + appkey 的安全原理
API 签名是防止 mall_id 被盗用的第一道防线。本文讲清 md5(mall_id + 业务参数 + tm + appkey) 签名的原理、5 个常见踩坑、与 HTTPS 的差异。
一句话定义
API 签名 = 用 mall_id + 业务参数 + 时间戳 + appkey 拼成字符串,再做 md5 哈希。 服务器收到请求后用同样的算法算签名,对比客户端传的签名,一致 = 请求合法。
一、为什么需要 API 签名
HTTPS 已经加密传输了,为什么还要签名?
| 防御目标 | HTTPS | API 签名 |
|---|
| 中间人窃听 | ✅ 防御 | ✅ 防御 |
|---|---|---|
| 请求篡改 | ❌ 无法 | ✅ 防御 |
| 重放攻击 | ❌ 无法 | ✅ 防御(配合 tm) |
| 身份冒用 | ❌ 无法 | ✅ 防御(配合 appkey) |
签名 = 防请求篡改 + 防重放 + 防冒用。HTTPS 只解决"加密传输",不解决"请求完整性"。
二、签名的 3 个核心要素
要素 1:mall_id(商户 ID)
要素 2:appkey(签名密钥)
要素 3:tm(时间戳)
三、签名生成原理(以银行卡四要素为例)
步骤 1:准备业务参数
| 参数 | 含义 | 示例 |
|---|
| mall_id | 商户 ID | MALL001 |
|---|---|---|
| realname | 姓名 | 张三 |
| idcard | 身份证号 | 110101199001011234 |
| cardnum | 银行卡号 | 6222021234567890123 |
| bankPreMobile | 银行预留手机号 | 13800138000 |
| tm | 13 位毫秒时间戳 | 1700000000000 |
上表 6 个业务参数里,只有 realname 和 cardnum 参与签名;idcard、bankPreMobile 要一起提交,但不参与签名计算。
步骤 2:按固定顺序拼接
sign = md5(mall_id + realname + cardnum + tm + appkey)
拼接顺序固定,不含 + 号。这是 1104 签名错误码最常见的踩坑点。
⚠️ 银行卡四要素(/v3/card4-server)的签名串只算 5 个字段:mall_id + realname + cardnum + tm + appkey。 idcard 和 bankPreMobile 虽然要一起提交,但不参与签名——把它们拼进签名串,结果一样是 1104。
同一套 MD5 规则,各接口参与签名的业务字段并不一样。照抄别的接口的公式是 1104 的头号来源:
| 接口 | 路径 | 签名公式 | 特别注意 |
|---|
| 身份证核验 | /v2/id-server | md5(mall_id + realname + idcard + tm + appkey) | 末位 X 转小写 x |
|---|---|---|---|
| 手机号二要素 | /phone/name | md5(mall_id + realname + phone + tm + appkey) | 不传身份证号 |
| 手机号三要素(详版) | /phone/three-detail | md5(mall_id + realname + idcard + tm + appkey) | ★不含 phone |
| 银行卡二/三/四要素 | /v3/card2-server 等 | md5(mall_id + realname + cardnum + tm + appkey) | ★不含 idcard、也不含 bankPreMobile |
| 短信验证码 | /sms.php | md5(mall_id + phone + code + appkey) | ★不含 tm;code 传内容原文 |
步骤 3:md5 哈希
import hashlib
raw = "MALL001" + "张三" + "6222021234567890123" + "1700000000000" + "your_appkey" # 银行卡四要素:不含 idcard/bankPreMobile sign = hashlib.md5(raw.encode()).hexdigest()
输出 32 位小写 hex 字符串
步骤 4:HTTP POST + sign 参数
POST /v3/card4-server HTTP/1.1
Host: api2.nztapi.com Content-Type: application/x-www-form-urlencoded
mall_id=MALL001&realname=张三&cardnum=6222021234567890123&idcard=110101199001011234&bankPreMobile=13800138000&tm=1700000000000&sign=a1b2c3d4e5f6...
步骤 5:服务端验证
服务端用同样的参数 + 算法计算签名,与客户端传的 sign 对比:
四、5 个常见踩坑
坑 1:拼接顺序错
症状:返回 1104
原因:不是 mall_id + appkey + tm + 业务参数,而是 mall_id + 业务参数 + tm + appkey
修法:严格按官方文档顺序
坑 2:加了 + 号
症状:返回 1104
原因:字符串里把连接符 + 也算进去了
修法:只拼接值,不含 +
坑 3:tm 用了秒级
症状:返回 1107 时间戳不合法
原因:必须 13 位毫秒(1700000000000),不是 10 位秒(1700000000)
修法:
tm = str(int(time.time() * 1000)) # ✅ 13 位
坑 4:appkey 写在客户端代码里
症状:安全漏洞
原因:appkey 一旦泄露,别人可以伪造你的请求(盗刷余额、冒用身份)
修法:
坑 5:tm 时间窗口太宽
症状:重放攻击成功
原因:服务端允许 ±30 分钟内的请求 → 攻击者截获请求后 30 分钟内可重放
修法:
五、与 HTTPS 的关系
| 维度 | HTTPS | API 签名 |
|---|
| 解决什么问题 | 加密传输 | 防篡改 + 防重放 + 防冒用 |
|---|---|---|
| 关键依赖 | SSL/TLS 证书 | appkey(签名密钥) |
| 性能开销 | 较大(TLS 握手) | 很小(一次 md5) |
| 是否必须 | 必须(传输安全) | 强烈推荐(业务安全) |
两者不是替代关系,而是互补关系。HTTPS 保证传输安全,签名保证请求完整性。
六、签名机制的局限
七、3 个安全最佳实践
1. appkey 定期轮换
2. IP 白名单
3. 调用频率限制
关于诺正通
诺正通 API 采用统一签名机制:md5(mall_id + 业务参数 + tm + appkey)。 支持 IP 白名单、appkey 轮换、调用频率限制 3 种安全策略。 100 次免费试用,按量计费,具体价格请联系商务。