2、令牌绑定的基本原理:密码学基础回顾、令牌与客户端绑定关系、绑定令牌的生命周期
好,咱们进入正题。令牌绑定(Token Binding)这个概念,说白了就是解决一个问题:令牌被偷了怎么办?
传统的 Bearer Token,谁拿着谁就能用。你想想看,这多危险。我曾在一次安全审计中遇到过,某家公司的 OAuth 令牌被中间人截获,攻击者拿着令牌直接接管了用户账号。嗯,从那以后,我对“令牌必须和客户端绑定”这件事就特别执着。
这一章,我们先打好基础。我会带你回顾几个关键的密码学概念,然后讲清楚令牌怎么跟客户端“绑”在一起,最后看看一个绑定令牌从生到死经历了什么。
2.1 密码学基础回顾:你只需要记住这三个东西
别怕,我不打算给你堆公式。做安全架构这么多年,我个人习惯把复杂的密码学抽象成几个核心概念。对于令牌绑定,你只需要搞懂三样:
- 非对称密钥对:公钥加密,私钥解密。公钥可以公开,私钥必须藏好。
- 数字签名:用私钥签名,用公钥验签。作用是证明“这东西是我发的,而且没被改过”。
- 密钥推导函数:从一个秘密值,派生出多个不同的密钥。比如 HKDF。
核心观点:令牌绑定的本质,就是让令牌持有者证明自己拥有某个私钥。没有私钥,令牌就是一张废纸。
为什么是这三个?因为绑定令牌的整个流程,就是围绕它们转的。客户端生成一对密钥,把公钥塞给服务器,服务器签发令牌时把公钥“锁”进令牌里。后续请求,客户端用私钥签名,服务器用公钥验签。完美闭环。
我的习惯:在实际项目中,我建议优先使用 P-256 椭圆曲线密钥对。它比 RSA 快,密钥更短,安全性也够。别问我为什么不用 RSA 4096,问就是性能开销太大,移动端扛不住。
2.2 令牌与客户端绑定关系:怎么“绑”才结实?
好,现在我们来聊聊“绑定”这件事。令牌和客户端之间,到底怎么建立关系?
我把它分为三个层次,你可以理解为三种“绑法”:
| 绑定层次 | 绑定方式 | 安全性 | 典型场景 |
|---|---|---|---|
| 弱绑定 | 基于客户端 IP、User-Agent 等 | 低(易伪造) | 传统 Web 应用 |
| 中绑定 | 基于 TLS 会话(Channel ID / Token Binding over TLS) | 中(依赖 TLS 层) | 浏览器环境 |
| 强绑定 | 基于应用层密钥证明(DPoP / mTLS) | 高(独立于传输层) | 移动端、API 网关 |
我个人最推荐的是强绑定。为什么?因为弱绑定太容易绕过,改个 IP 就能冒充。中绑定虽然好一点,但一旦 TLS 中断(比如代理、负载均衡),绑定关系就断了。
强绑定怎么做?我给你画个流程图,一看就明白。
看到了吗?关键在第②步和第④步。服务器签发令牌时,把公钥的哈希值(指纹)写进令牌里。客户端每次请求,都要用私钥签名。服务器验签通过,才放行。
避坑指南:我曾经见过一个团队,把整个公钥直接塞进令牌里。结果令牌体积暴涨,每次请求都多传几百字节。正确做法是只存公钥的 SHA-256 指纹,服务器查数据库匹配完整公钥。省带宽,也安全。
2.3 绑定令牌的生命周期:从生到死
一个绑定令牌,不是永生的。它有自己的生命周期。我把它分成五个阶段:
- 注册阶段:客户端生成密钥对,向服务器注册公钥。
- 签发阶段:服务器验证客户端身份后,签发绑定令牌。
- 使用阶段:客户端用私钥签名,携带令牌访问资源。
- 刷新阶段:令牌快过期时,用旧令牌换新令牌(同时证明私钥还在)。
- 吊销阶段:私钥泄露或客户端失活,服务器吊销令牌。
这里有个细节,很多人会搞错。刷新令牌时,要不要重新证明私钥所有权?
我的答案是:必须证明。否则,攻击者偷了你的刷新令牌,就能无限续期。我在一个金融项目中就遇到过这种设计缺陷,后来花了整整两周才修复。
最佳实践:刷新令牌时,要求客户端用原私钥签名一个“刷新请求”。服务器验签通过后,才签发新的绑定令牌。这样即使刷新令牌被盗,没有私钥也白搭。
最后,我整理了一张生命周期状态表,方便你对照:
| 阶段 | 状态 | 客户端行为 | 服务器行为 |
|---|---|---|---|
| 注册 | 未激活 | 生成密钥对,发送公钥 | 存储公钥,返回注册凭证 |
| 签发 | 活跃 | 携带注册凭证请求令牌 | 签发绑定令牌(含公钥指纹) |
| 使用 | 活跃 | 私钥签名,携带令牌 | 验签,放行或拒绝 |
| 刷新 | 活跃→新活跃 | 用旧私钥签名刷新请求 | 验签,签发新令牌,吊销旧令牌 |
| 吊销 | 已吊销 | 停止使用 | 加入黑名单,拒绝请求 |