安全要求
本页所有内容都是对你的对接的要求。没有一条是系统替你强制执行的。
保护好 client secret
Client secret 等同于你应用的身份。任何拿到它的人都能换取签发给你的授权码,也能以你的 名义刷新令牌。
- 放在服务器上。永远不要打进浏览器产物、移动端二进制、公开仓库或客户端配置文件。
- 从密钥管理服务或环境变量加载,不要写进源码。
- 怀疑泄露就在开发者页面重新生成。先部署新值 —— 重新生成立即生效,任何还持有旧值
的进程会立刻开始收到
invalid_client。
如果你的应用无法保存密钥,请改用 PKCE,并接受客户端类型里 列出的限制。
每次都校验 state
每次发起授权都生成一个随机 state,绑定到用户会话,回调时 state 对不上就拒绝。
不这么做,攻击者可以用自己的 Nanako 账号走完一次授权,再把得到的回调 URL 交给你的 用户,从而把受害者的会话关联到攻击者的身份上。
取值要保持 URL 安全 —— 十六进制或无填充 base64url。它会被原样拼进回调 URL,不做转义,
所以 &、#、= 和空格都会破坏这次跳转。
使用 PKCE
PKCE 的成本是一次哈希,收益是关掉了"授权码泄露后被别人换走"的窗口 —— 泄露渠道可能是
代理日志、浏览器历史或 Referer 头。
请发送 code_challenge_method=S256,必须是这个精确写法;s256 会静默回落到 plain,
不提供任何保护。
机密客户端同样受益。但要注意:如果你同时用了 PKCE 和 client secret,带 challenge 签发 的授权码是由 challenge 来校验的,所以请像保管密钥一样保管 verifier。
注册精确的回调地址
回调地址逐字节比对,这正是你想要的行为。不要试图绕开它:
- 面向用户的地址一律用
https://。http://在注册时被接受是为了本地开发 —— 永远不要注册一个公网可达的明文地址。 - 每个真实回调注册一个地址。不要注册一个宽泛的地址再在内部转发。
- 每次请求的附带数据放在
state里,不要放进回调地址。带查询字符串的回调地址会让整个 回调失效。
把令牌当作凭证处理
- 永远不要打日志。 access token 和 refresh token 都是 bearer 凭证,持有即可冒充用户。
- 加密存储,并按所属用户隔离。
- 不要放进 URL。 access token 走
Authorization头。 - 不要解析。 它们是不透明字符串,里面没有可读结构。
Access token 有效期两小时。Refresh token 自首次授权起 30 天,且不会因使用而延长 —— 请为"整条链结束、用户需要重新授权"这件事做好准备。
串行化刷新
Refresh token 每次使用都会轮换,而提交一个已经轮换过的 token 会被判定为重放:该用户在 该应用下的所有令牌会被撤销,用户被踢出你的应用。
两条规则可以避免:
- 先把新的 refresh token 持久化,再用响应里的其他内容。收到与写入之间崩溃一次, 代价就是用户的会话。
- 每个用户同时只允许一次刷新。 加锁,或者由单一的定时 worker 主动提前刷新,而不是 在每个请求处理器里被动刷新。
校验实际授予的 scope
token 响应里的 scope 是用户真正批准的范围。请读取它,并据此约束你自己的行为。不要
假设请求发出去是什么就被授予了什么。
尊重撤销
用户可以随时在 Nanako 账号里撤回你的应用。这件事没有回调通知 —— 你会在下一次刷新收到
invalid_grant 时才知道。
把它当作"授权已收回":删除已存的令牌和缓存的用户资料,重新展示登录入口。不要重试,也 不要继续用缓存数据服务这个用户。
当你自己的应用让用户退出登录时,请显式撤销令牌,而不是只把它丢掉。见 第 6 步。
用 sub 建关联,不要用邮箱
sub 是稳定标识。邮箱和手机号可以更换、释放、被别人重新使用;昵称可以随意改。以邮箱
做匹配的账号体系,等于埋了一颗账号接管的雷。
首次登录时把 sub 存为外键,之后一律按它匹配。
浏览器端应用
第三方网页来源无法直接调用 /oauth/token 或 /oauth/userinfo。Nanako 的 CORS 白名单
由运营方配置且没有通配符,浏览器会拦掉响应。
更麻烦的是,这个拦截并不干净。token 请求用的是 CORS 安全内容类型,所以请求会真的发出 并被处理 —— 授权码在服务端已经被消费 —— 之后浏览器才丢弃你的脚本读不到的响应。也就是 每尝试一次就烧掉一个一次性授权码,失败之后无法重试。
请把换取令牌这一步放在你的服务端。如果确实必须做纯浏览器架构,动手之前请先联系我们。
Nanako 这一侧做了什么
供你评估威胁模型参考,提供方侧的情况:
- Access token 和 refresh token 以 SHA-256 摘要存储,数据库里没有可被重放的原值。
- Client secret 以 bcrypt 哈希存储,创建之后无法还原。
- Refresh token 每次使用都轮换,重放已轮换的 token 会撤销该用户在该应用下的整条链。
- 授权码与客户端、回调地址和用户绑定,10 分钟过期,只能使用一次。
- authorize、token、userinfo 事件都会记录时间、IP 和设备类型,用户可在账号设置里查看。
- 每个响应都带
X-Frame-Options: DENY和禁止内嵌的 CSP,流程无法被 iframe 套用。