Next.js authorization bypass vulnerability
Research is free — Hunters explains how the bug works, the root-cause code pattern, how the fix addresses it, and how to test whether a target is affected, in chat. Investigate & write exploit is a paid run — the engine reads the advisory and fix commits, then builds and validates a working proof-of-concept exploit with reproduction steps.
Affected versions
Details
### Impact If a Next.js application is performing authorization in middleware based on pathname, it was possible for this authorization to be bypassed. ### Patches This issue was patched in Next.js `14.2.15` and later. If your Next.js application is hosted on Vercel, this vulnerability has been automatically mitigated, regardless of Next.js version. ### Workarounds There are no official workarounds for this vulnerability. #### Credits We'd like to thank [tyage](http://github.com/tyage) (GMO CyberSecurity by IERAE) for responsible disclosure of this issue.
The fix
backport: [i18n] Routing fix #70761 (#70976)
packages/next/src/server/base-server.ts+8 −0
@@ -908,6 +908,14 @@ export default abstract class Server<ServerOptions extends Options = Options> {req.headers['x-forwarded-proto'] ??= isHttps ? 'https' : 'http'req.headers['x-forwarded-for'] ??= originalRequest.socket?.remoteAddress+// Validate that if i18n isn't configured or the passed parameters are not+// valid it should be removed from the query.+if (!this.i18nProvider?.validateQuery(parsedUrl.query)) {+delete parsedUrl.query.__nextLocale+delete parsedUrl.query.__nextDefaultLocale+delete parsedUrl.query.__nextInferredLocaleFromDefault+}+// This should be done before any normalization of the pathname happens as// it captures the initial URL.this.attachRequestMeta(req, parsedUrl)
packages/next/src/server/future/helpers/i18n-provider.ts+31 −0
@@ -134,6 +134,37 @@ export class I18NProvider {}}+/**+* Validates that the locale is valid.+*+* @param locale The locale to validate.+* @returns `true` if the locale is valid, `false` otherwise.+*/+private validate(locale: string): boolean {+return this.lowerCaseLocales.includes(locale.toLowerCase())+}++/**+* Validates that the locales in the query object are valid.+*+* @param query The query object to validate.+* @returns `true` if the locale is valid, `false` otherwise.+*/+public validateQuery(query: NextParsedUrlQuery) {+if (query.__nextLocale && !this.validate(query.__nextLocale)) {+return false+}++if (+query.__nextDefaultLocale &&+!this.validate(query.__nextDefaultLocale)+) {+return false+}++return true+}+/*** Analyzes the pathname for a locale and returns the pathname without it.*
packages/next/src/server/lib/router-utils/resolve-routes.ts+5 −0
@@ -214,6 +214,11 @@ export function getResolveRoutes(parsedUrl.pathname = maybeAddTrailingSlash(parsedUrl.pathname)}}+} else {+// As i18n isn't configured we remove the locale related query params.+delete parsedUrl.query.__nextLocale+delete parsedUrl.query.__nextDefaultLocale+delete parsedUrl.query.__nextInferredLocaleFromDefault}const checkLocaleApi = (pathname: string) => {
References
- WEBhttps://github.com/vercel/next.js/security/advisories/GHSA-7gfc-8cq8-jh5f
- ADVISORYhttps://nvd.nist.gov/vuln/detail/CVE-2024-51479
- WEBhttps://github.com/vercel/next.js/commit/1c8234eb20bc8afd396b89999a00f06b61d72d7b
- PACKAGEhttps://github.com/vercel/next.js
- WEBhttps://github.com/vercel/next.js/releases/tag/v14.2.15