#4676·omnivore

SSO 重定向 URL 中暴露的非过期身份验证凭证 (?tok=) 可实现帐户劫持

作者: 28Hus创建于 2026年8月26日更新于 2026年8月28日

□ 总结

默认情况下 (prod' appEnv / Vercel) Google的登录流量,API将** 永久** 网络认证 JWT(AuthToken',Auth' cookie value)放入重定向URL中的SSO JWT内,作为"/api/client/auth?tok=<ssoToken>". JWT有效载荷是签名的,没有加密,因此任何人若观察到URL(访问日志,CDN/proxy/Vercel日志,浏览器历史,HAR导出,共享链接)可以解码"tok"参数并恢复"authToken"——这种证书永远不会过期,被API接受,没有会话约束或撤销. 这样就能够持续地接管账户。

□ 影响和先决条件

  • ** 影响**:完全、永久账户接管(阅读文章/说明、管理订阅、运行经认证的GraphQL业务)。 证书不能因密码更改或登录而失效——只能通过旋转`JWT SECRET'。
  • ** 先决条件**:
  1. 部署使用默认的网络app配置(NEXT PUBLIC APP ENV' unset或prod'),所以Google的登录帖子是/api/auth/Vercel/gauth-redict' (isVercel= true')。 这是正式部署。
  2. " SSO-JWT-SECRET " 在API和网络上配置(标准键为`.env.template'/自宿 '.env.example " )。
  3. 受害人与Google登录。
  4. 攻击者获得 " tok " URL或载有该URL的日志/历史记录。
  • ** 严重性**:中等(以URL观测为条件;影响是永久性的完全接管)。

□ 根源

" packages/api/src/routers/auth/jwt helpers.ts " —— " 创建WebAuthToken " 标志 使用 " JWT-SECRET " 和 " 无 " 过期 InQQ (无`exp ' 类索赔):

导出 aync 函数创建WebAuthToken( 用户ID: 字符串): 保证 < string QQ 未定义 > { const auuthToken = 等待 signToken ({ uid: userId}}, env.server.jwtSecret) // 没有exp - > 永远不会过期 返回认证为字符串 {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}你觉得呢?


`包/api/src/utils/sso.ts'——永久`authToken'嵌入SSO JWT并放入URL查询字符串:


导出 const 创建 SsoToken = (authToken: 字符串, 重定向为: 字符串): 字符串 {{
const ssotoken = jwt. sign ({ 认证Token, 重定向到}, env. server.sso JwtSecret, { 过期在'1d'})
返回所选
{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}你觉得呢?

导出 const ssoRedirectURL = (ssoToken: string): 字符串 {{{}
const u = 新的 URL( homePageURL ())
u.pathname='api/客户端/自动'
u.searchPrams.append ('tok', ssoToken) // 证书输入 URL 查询参数
返回 u.to String ()
{\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}你觉得呢?

“ 过期” : “ 1d” 仅限定“ SSO” 包装; 嵌入式“ authToken” 未过期 。

" 包装/api/src/routers/auth/google auth.ts " ——在 "isVercel " 分支中被触发:

如果( 是 Vercel) { 缩写 ssotoken = 创建 Ssotoken(authToken, 重定向URL) 重定向URL = 重定向URL( ssoToken) {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}你觉得呢?


`包装/网页/网页/api/客户端/auth.ts ' ——网络端点验证SSO标志并将嵌入式证书写作`auth' cookie:


const tok = req.query.tok (中文(简体) ).
如果(sso JwtSecret QQ tok !Array.is Array(tok)) {(请检查date=中的日期值)
const 有效载荷 = jwt.virify(tok, ssoJwtSecret) 为 AuthPay 载荷
. . . . . . .

内容来源: omnivore-app/omnivore