常见安全问题
redirect_uri 校验、token 泄露、implicit 废弃。
常见安全问题
这一课你将能做到
- 拿着清单逐项自查:state、PKCE、redirect_uri 白名单、HTTPS、secret 存放、token 存储与日志脱敏
- 说清 Implicit 与 Resource Owner Password 两种模式被废弃的原因,能在代码评审时一票否决
- 制定 token 泄露应急预案:识别 → 轮换 secret / 撤销 refresh → 强制重新授权 → 复盘日志
为什么要学
OAuth 配错的后果不是「登录按钮没反应」,而是 账号劫持、开放重定向、token 盗用 这类能上新闻的事故。绝大多数事故的根因是实现偷懒:
- state 固定成字符串「xyz」→ CSRF 防线归零;
- redirect_uri 用前缀匹配甚至通配 → code 被送到攻击者站点;
- client_secret 写进前端环境变量 → 机密公开;
- access_token 有效期 30 天且无撤销 → 一次泄露 = 一个月敞开的大门。
本课把前面 10 课散落的安全点收拢成一份上线门禁清单:联邦登录或开放 API 上线前,逐项打勾,勾不完不放行。
先建立图像
攻击面 防护锁
──────────────────────────────────────────────
伪造 callback(CSRF) → state:随机 + 会话绑定 + 一次性
截获 code 换票 → PKCE(公共客户端)/ client_secret(机密客户端)
偷换回调地址 → redirect_uri 精确白名单 + 禁开放重定向
窃听传输 → 全程 HTTPS(生产)
XSS 偷凭证 → HttpOnly cookie + 凭证不进 JS 可读存储 + CSP
scope 过大 → 最小权限申请 + API 端强制校验
凭证长寿 → 短 access + refresh 轮换 + 撤销端点
secret 泄露 → 环境变量/密钥管理 + 仓库扫描 + 轮换预案
每一行都是前面某课的一个知识点——安全不是一门新学问,而是把已学的纪律逐条落实。
跟着做
步骤 1:审查 redirect_uri 白名单
✅ 合法示例:
https://app.example.com/oauth/callback
⚠️ 危险倾向:
https://app.example.com/oauth/callback.evil.com (若匹配逻辑是前缀)
https://evil.com/…?next=https://app.example.com/… (借开放重定向中转)
允许通配 * 或任意追加 query (等于没有白名单)
规则:IdP 控制台只保留精确 URI(全等比较,含协议、域名、端口、路径);本地与生产分两个 OAuth App;代码里跳转逻辑禁止接收任意 URL 参数作为 redirect 目标。
步骤 2:确认 state 与 PKCE 同时启用(门禁伪码)
// 中文注释:上线前对照——两个防护缺一不可
void startLogin(HttpSession session) {
session.setAttribute("oauth_state", randomToken()); // 随机 + 一次性
session.setAttribute("pkce_verifier", randomVerifier()); // 公共客户端必配
}
void onCallback(String state, String code, HttpSession session) {
requireEquals(state, session.take("oauth_state")); // 校验后立即删除
// 换 token 必须带 verifier(公共)或 client_secret(机密)
}
步骤 3:扫描仓库里是否泄露 secret
# 中文注释:仓库根目录自查;发现真实值立即到 IdP 后台轮换 secret
rg -n "GITHUB_CLIENT_SECRET|client-secret:\s*['\"]?[A-Za-z0-9]" \
--glob '!**/node_modules/**' --glob '!**/target/**'
# 前端项目再查一遍「公开前缀」环境变量里是否混进了机密
rg -n "NEXT_PUBLIC_.*(SECRET|SECRET_KEY|CLIENT_SECRET)" .