PHP加密API限流令牌_加密令牌生成验证防刷接口【方法】

6次阅读

防重放令牌必须用hash_hmac()带密钥生成,结构为timestamp|user_id|nonce,nonce单次有效且redis原子校验,时间偏差±300秒内有效,敏感字段需aes-256-cbc加密,密钥严禁硬编码,header推荐x-api-Token避免nginx透传问题。

PHP加密API限流令牌_加密令牌生成验证防刷接口【方法】

phphash_hmac()生成防重放的加密令牌

直接用md5()sha1()拼接时间戳+用户ID生成令牌,等于没加密——攻击者截获一次就能重放。必须带密钥、带时效、带唯一性约束。hash_hmac()是PHP内置安全选择,比手写base64_encode(sha1($key.$data, true))更可靠。

  • 密钥必须存于环境变量或配置文件,**绝不能硬编码在代码里**;示例:$_ENV['API_TOKEN_KEY']
  • 令牌结构建议:timestamp|user_id|nonce(如1717023456|u_8821|a7f3e9),其中noncebin2hex(random_bytes(3))生成,单次有效
  • 生成后立即把nonce存入Redis(带过期,比如EX 300),验证时先查是否存在并DEL,防止重复提交
  • 时间戳偏差需校验:服务端当前时间与令牌中时间差超过±300秒即拒收,防拖库重放

限流逻辑必须和令牌验证耦合,不能分两步走

很多实现先验令牌再查Redis限流计数,中间存在竞态窗口——攻击者可并发发10个合法令牌绕过限制。正确做法是把「令牌有效性」和「当前用户请求次数」合并为一个原子操作。

  • 用Redis的EVAL执行lua脚本:先GET令牌对应nonce确认未使用,再INCR该用户+时间窗口的计数器,最后EXPIRE整个key;失败则返回nil
  • PHP调用示例:$redis->eval($lua_script, 3, 'rate:u_8821:20240530', 'nonce:a7f3e9', 'token:1717023456|u_8821|a7f3e9')
  • 不要用file_get_contents()curl去调第三方限流服务——网络延迟会让原子性失效,且增加单点故障

openssl_encrypt()不是必须,但对敏感字段要加密而非仅签名

如果令牌里要携带权限等级、设备指纹等敏感信息(而不仅是防重放),仅hash_hmac()签名不够——攻击者虽不能篡改,但能解码看到明文。此时必须加密。

  • openssl_encrypt()而非mcrypt(已废弃)或自研异或加密;算法固定用AES-256-CBC,IV必须随机且随密文一起传输(base64后拼在密文末尾)
  • 密钥仍来自环境变量,且**每次加密必须用新IV**:$iv = random_bytes(openssl_cipher_iv_length('AES-256-CBC'))
  • 解密失败(返回false)或解密后json结构非法,一律按非法令牌处理,不抛异常、不打日志细节(防侧信道)

前端传令牌时别踩Authorization头格式坑

很多前端开发者习惯写Authorization: Bearer xxx,但你的PHP后端若用getallheaders()手动解析,会因apache/Nginx配置差异漏掉该头——尤其Nginx默认不透传Authorization

立即学习PHP免费学习笔记(深入)”;

  • 最稳方案:前端改用自定义头,如X-Api-Token: xxx,服务端用$_SERVER['HTTP_X_API_TOKEN']直接取(注意下划线转大写)
  • 若坚持用Authorization,Nginx需加配置:fastcgi_pass_request_headers on; + underscores_in_headers on;(后者慎开,可能影响其他逻辑)
  • 别在$_GET$_POST里传令牌——URL会被CDN、代理、浏览器历史记录留存,日志里也容易泄露

实际最难的部分不是生成或验证,而是密钥轮换时如何平滑过渡:旧密钥签发的令牌还没过期,新密钥已启用。得在验证逻辑里支持双密钥校验,并设好旧密钥的强制下线时间——这个时间点一旦错过,就只能靠延长所有令牌有效期来补救。

text=ZqhQzanResources