目标
授权码流程对公共客户端有个结构性弱点:code 要经过其他应用可截获的重定向机制传递。PKCE 把 code 焊死在攻击者从未拿到的东西上——每次请求随机生成的 verifier,其哈希随请求走、其原值在换码时证明。被截获的 code 于是成了一张贼无法完成的交易回执。
步骤要点
- 每次授权尝试生成 32 字节 base64url 的 codeVerifier,并计算 SHA-256 的 codeChallenge。
- 授权请求携带 code_challenge 与 code_challenge_method=S256,授权服务器把签发的 code 绑定到该挑战值。
- 换码请求带 authorization_code + code_verifier,服务器哈希比对,无 verifier 的截获 code 必然失败。
- 策略上只允许 S256:出现 plain 方法即拒绝,杜绝挑战值即答案的降级。
- 用错误 verifier 兑换同一 code 实测:应得 400 invalid_grant,证明赃码无效。
排错提示
服务器拒绝 S256 说明其 PKCE 支持需按客户端启用或版本过旧,先查元数据里的 code_challenge_methods_supported;只做 PKCE 不带 state 是混了两种攻击面,PKCE 防 code 被盗用、state 防 CSRF 并携带关联,缺一不可;verifier 只需活到换码为止,放内存随请求走,勿落持久存储被后续读取复用。
适用边界
PKCE 保护的是换码环节,救不了被攻陷的授权服务器或被恶意注册的重定向;机密客户端还应叠加客户端认证,PKCE 作为纵深防御依然值得。
代码示例
// Per attempt
const codeVerifier = crypto.randomBytes(32).toString("base64url");
const codeChallenge = crypto.createHash("sha256")
.update(codeVerifier).digest("base64url");
// authorize?...code_challenge=...&code_challenge_method=S256
// token: code + code_verifier (the proof)