后端接口签名验证:筑牢 API 安全防线

218 阅读2分钟

在后端 API 开发中,接口的安全性至关重要,尤其是对外开放的接口,容易遭受伪造请求、参数篡改等攻击。接口签名验证机制,通过对请求参数进行加密签名,确保请求的真实性和完整性,是抵御这类攻击的有效手段。

签名验证的核心原理

签名验证的核心逻辑是:客户端与服务端约定一套签名规则,客户端发送请求时,根据规则生成签名并随请求一起发送;服务端收到请求后,用同样的规则重新生成签名,与客户端传来的签名比对,一致则认为请求有效,否则拒绝。

签名生成的关键步骤

1. 确定参与签名的参数

并非所有参数都需要参与签名,通常包括:

  • 业务参数(如订单号、金额等核心数据)

  • 时间戳(timestamp):防止请求被重放(如限制 10 分钟内有效)

  • 随机数(nonce):避免签名被预测

注意:文件、图片等二进制参数不参与签名,签名本身、token 等也不参与。

2. 参数排序与拼接

为避免因参数顺序不同导致签名不一致,需对参数按 Key 进行 ASCII 升序排序,然后拼接成 “key=value&key=value” 的字符串。

示例:参数{ "orderNo":"123", "amount":100, "timestamp":1620000000 }排序后拼接为amount=100&orderNo=123×tamp=1620000000

3. 加入密钥并加密

在拼接后的字符串末尾加入服务端与客户端约定的密钥(secret),然后使用 MD5、SHA256 等哈希算法加密,生成签名。

代码示例

public String generateSign(Map<String, String> params, String secret) {
    // 1. 参数排序
    List<String> keys = new ArrayList<>(params.keySet());
    Collections.sort(keys);
    
    // 2. 拼接字符串
    StringBuilder sb = new StringBuilder();
    for (String key : keys) {
        String value = params.get(key);
        if (value != null && !value.isEmpty()) {
            sb.append(key).append("=").append(value).append("&");
        }
    }
    // 3. 加入密钥
    sb.append("secret=").append(secret);
    
    // 4. SHA256加密
    return DigestUtils.sha256Hex(sb.toString());
}

4. 服务端验证流程

服务端收到请求后:

  1. 提取参数和客户端传来的签名(sign)
  2. 验证时间戳是否在有效期内(如当前时间 - timestamp < 10分钟
  3. 验证随机数(nonce)是否已使用过(防止重放,可存入 Redis 设置短期过期)
  4. 按客户端同样的规则生成签名,与客户端 sign 比对
  5. 全部通过则处理请求,否则返回 “签名无效”

避坑指南

  • 密钥管理:密钥需妥善保管,客户端加密存储,服务端定期轮换
  • 敏感参数加密:签名只能防篡改,不能防泄露,敏感参数(如密码)需额外加密传输
  • 避免明文传输:签名验证需配合 HTTPS 使用,防止参数和签名被中间人窃取