你的 Auth.js 可能正被利用三大漏洞一次性排查邮件链接劫持、Token 崩溃、OAuth 账户关联 (附实战修复)三个必须立刻处理的漏洞先快速对号入座编号严重性核心问题影响版本next-authGHSA-7rqj-j65f-68whCritical邮箱规范化顺序错误导致登录链接可被截胡 4.0.0 且 修复版GHSA-xmf8-cvqr-rfgjHighgetToken()因畸形 Header 抛异常导致 DoS 5.0.0-beta.25GHSA-x445-f3h2-j279MediumOAuth 多 provider 状态下 state/nonce 未绑定可致账户关联劫持5.0.0-beta.1 ~ 5.0.0-beta.31下面我会把每个漏洞掰开揉碎讲附带真实场景的代码示例以及我最推荐的修法。一、GHSA-7rqj-j65f-68wh一个 Unicode 字符让魔法链接进了别人邮箱到底发生了什么这个漏洞非常“刁钻”。它出在email登录方式即密码less 的 magic link的邮箱地址规范化流程上。正常逻辑应该是用户输入邮箱地址做 Unicode 规范化NFKC校验是否只有一个符号生成链接并发送但 Auth.js 旧版本把顺序搞反了——先校验后规范化。结果就是攻击者可以构造一个包含特殊 Unicode 字符的邮箱地址这个字符在 NFKC 规范化后会变成一个。比如UFF20全角 at在 NFKC 下会变成普通的U0040。攻击者输入victimexample.com第一步的校验只检查“原始字符串里是否只有一个 ASCII 的 ”这里没有所以通过。然后下游的邮件发送库比如 nodemailer 或 AWS SES在处理收件人地址时会做 NFKC 规范化于是地址变成了victimexample.com看上去没毛病对吧但攻击者真正填的是attacker-controlled-partvictims-domain.com规范化后attacker-controlled-partvictims-domain.com那个被“翻译”成了而校验时只看到一个 ASCII 的就放行了。结果就是——魔法链接发到了攻击者的邮箱攻击者拿到链接后就能以受害者身份登录。影响有多严重无需受害者交互攻击者只要知道受害者的邮箱地址就可以发起攻击。在支持国际化邮箱SMTPUTF8的环境下这个漏洞几乎通杀因为大部分现代邮件发送服务都会做规范化。如果你们的系统支持邮箱登录passwordless 或 magic link且没有自定义normalizeIdentifier那基本确定在坑里。修法升级 自定义归一化官方已经在最新版具体版本号留意 GitHub release修复了这个问题优先升级无需改业务代码。如果暂时升不了立刻加自定义normalizeIdentifierimportNextAuthfromnext-auth;importEmailProviderfromnext-auth/providers/email;exportconstauthOptions{providers:[EmailProvider({// 自定义规范化函数normalizeIdentifier(identifier:string){// 1. NFKC 归一化letnormalizedidentifier.normalize(NFKC).toLowerCase().trim();// 2. 严格校验只能有一个 constpartsnormalized.split();if(parts.length!2){thrownewError(Invalid email address);}// 可选若不需要国际化邮箱直接拒绝非 ASCII// if (/[^\x00-\x7F]/.test(normalized)) throw new Error(ASCII only);returnnormalized;},}),],};这样确保校验发生在规范化之后把这种“二义性”字符提前挡掉。二、GHSA-xmf8-cvqr-rfgj一个畸形的 Bearer Token把你的 API 打成 500问题现场还原我们经常在 API Route 里这样用import{getToken}fromnext-auth/jwt;exportasyncfunctionGET(req:NextRequest){consttokenawaitgetToken({req,secret:process.env.NEXTAUTH_SECRET});if(!token)returnnewResponse(Unauthorized,{status:401});// ... 正常业务}看着没什么问题对吧但假如攻击者发来一个Authorization: Bearer %头。注意后面只有一个%没有合法的百分号编码。这时候getToken()内部会先对 header 做decodeURIComponent()而decodeURIComponent(%)会直接抛出URIError。因为这个异常没有被getToken()内部捕获它会一路冒泡到你的路由处理函数于是你的 API 直接 500整个服务不可用至少这个接口废了。攻击者只需要发起一个简单请求就能让你的鉴权中间件崩溃。影响面有多大只要你直接调用了getToken()且没有用 try/catch 包裹就中招。auth()辅助函数不受影响因为它内部已经做了兜底。很多项目在 middleware 里用getToken()做全局鉴权这一炸就是全局炸。修复方案升级 or 手写 try/catch官方修复版本已发布升级后getToken()内部会捕获URIError并将畸形 token 当作null返回行为对齐其他无效 token。来不及升级的话立即在所有getToken()调用处加 try/catchlettokennull;try{tokenawaitgetToken({req,secret:process.env.NEXTAUTH_SECRET});}catch{tokennull;// 任何异常都视为无 token}if(!token)returnnewResponse(Unauthorized,{status:401});// 继续业务...更优雅的做法是封装一个安全的safeGetToken工具函数全局替换import{getTokenas_getToken}fromnext-auth/jwt;exportasyncfunctionsafeGetToken(req:NextRequest){try{returnawait_getToken({req,secret:process.env.NEXTAUTH_SECRET});}catch{returnnull;}}然后把所有getToken引用都改成safeGetToken一劳永逸。三、GHSA-x445-f3h2-j279多 Provider 下的“狸猫换太子”攻击链拆解这个漏洞发生在多 OAuth/OIDC 提供商场景下且允许用户在已登录状态下绑定新的第三方账号常见于“绑定 GitHub / Google 账户”这类功能。核心原因是Auth.js 把 OAuth 流程中的state、nonce和 PKCEverifier统一放在全局 cookie 里没有和发起登录的 provider 绑定。于是攻击者可以这样做受害者已登录你的网站比如用邮箱登录。攻击者诱导受害者点击一个链接该链接会用受害者的浏览器发起一个到 Provider A比如 Google的 OAuth 授权请求。同时攻击者自己在另一个浏览器中发起 Provider B比如 GitHub的授权请求并截获 GitHub 返回的code。因为 Auth.js 的 callback 端点只检查 cookie 里有没有state/nonce而不验证这个state到底是哪个 provider 生成的攻击者拿着 GitHub 的code往你的 callback 端点一丢配合受害者 cookie 里的state来自 Google就成功将攻击者的 GitHub 账号绑定到了受害者的用户 ID 上。从此攻击者可以用自己的 GitHub 登录你的系统直接进入受害者的账号。现实难度与风险等级虽然被定为 Medium但它的前提条件不少必须多 provider 且支持 account linking至少有一个 provider 不使用 PKCE只用 state/nonce需要受害者主动点链接但可以配合社工或 XSS 等方式不过一旦得手就是持久化的账户接管且受害者很难察觉。修复升级 辅助措施官方修复版本已把state/nonce/verifier与 provider 的id/issuer绑定callback 校验时会比对来源拒绝跨 provider 使用。如果暂时无法升级可以强制所有 OAuth 提供商启用 PKCE检查参数checks: [pkce]PKCE 的code_verifier无法被跨 provider 复用可以阻断这种攻击。禁止登录后绑定新 provider或者提高绑定时的安全验证比如二次密码确认。对linkAccount事件做详细审计日志并发送通知邮件给用户万一发生异常用户可以及时发现并解绑。总结一下我个人的修复优先级漏洞是否紧急我的建议GHSA-7rqj-j65f-68wh极高立即升级或加自定义normalizeIdentifier这是 account takeover不能拖GHSA-xmf8-cvqr-rfgj高加 try/catch 只需要改几行代码立刻做避免被人扫到导致服务中断GHSA-x445-f3h2-j279中如果不支持多 provider 绑定基本不受影响否则建议至少加 PKCE 和审计最后给个贴心提示升级前务必看 release note这几个漏洞的 patch 版本可能有些 breaking change尤其是 beta 版建议先在测试环境跑通全流程登录、绑定、session 刷新再上生产。修复完这几个坑之后Auth.js 总体上还是我最推荐的 Next.js 认证方案只是安全更新一定要追得勤一点。有问题欢迎评论区讨论我会尽力回复。如果觉得有用点个赞或收藏让更多遇到同样问题的同学看到。