Authorization Bypass in Next.js Middleware
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 It is possible to bypass authorization checks within a Next.js application, if the authorization check occurs in middleware. # Patches * For Next.js 15.x, this issue is fixed in `15.2.3` * For Next.js 14.x, this issue is fixed in `14.2.25` * For Next.js 13.x, this issue is fixed in 13.5.9 * For Next.js 12.x, this issue is fixed in 12.3.5 * For Next.js 11.x, consult the below workaround. _Note: Next.js deployments hosted on Vercel are automatically protected against this vulnerability._ # Workaround If patching to a safe version is infeasible, we recommend that you prevent external user requests which contain the `x-middleware-subrequest` header from reaching your Next.js application. ## Credits - Allam Rachid (zhero;) - Allam Yasser (inzo_)
The fix
Update middleware request header (#77201)
packages/next/src/server/lib/router-server.ts+7 −4
@@ -166,10 +166,13 @@ export async function initialize(opts: {renderServer.instance =require('./render-server') as typeof import('./render-server')-const allowedOrigins = [-'localhost',-...(config.experimental.allowedDevOrigins || []),-]+const randomBytes = new Uint8Array(8)+crypto.getRandomValues(randomBytes)+const middlewareSubrequestId = Buffer.from(randomBytes).toString('hex')+;(globalThis as any)[Symbol.for('@next/middleware-subrequest-id')] =+middlewareSubrequestId++const allowedOrigins = ['localhost', ...(config.allowedDevOrigins || [])]if (opts.hostname) {allowedOrigins.push(opts.hostname)}
packages/next/src/server/lib/server-ipc/utils.ts+11 −0
@@ -57,5 +57,16 @@ export const filterInternalHeaders = (if (INTERNAL_HEADERS.includes(header)) {delete headers[header]}++// If this request didn't origin from this session we filter+// out the "x-middleware-subrequest" header so we don't skip+// middleware incorrectly+if (+header === 'x-middleware-subrequest' &&+headers['x-middleware-subrequest-id'] !==+(globalThis as any)[Symbol.for('@next/middleware-subrequest-id')]+) {+delete headers['x-middleware-subrequest']+}}}
packages/next/src/server/web/sandbox/context.ts+4 −0
@@ -373,6 +373,10 @@ Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation`),store.headers.get('x-middleware-subrequest') ?? '')}+init.headers.set(+'x-middleware-subrequest-id',+(globalThis as any)[Symbol.for('@next/middleware-subrequest-id')]+)const prevs =init.headers.get(`x-middleware-subrequest`)?.split(':') || []
test/e2e/middleware-general/test/index.test.ts+13 −0
@@ -144,6 +144,19 @@ describe('Middleware Runtime', () => {}}+it('should filter request header properly', async () => {+const res = await next.fetch('/redirect-to-somewhere', {+headers: {+'x-middleware-subrequest':+'middleware:middleware:middleware:middleware:middleware',+},+redirect: 'manual',+})++expect(res.status).toBe(307)+expect(res.headers.get('location')).toContain('/somewhere')+})+it('should handle 404 on fallback: false route correctly', async () => {const res = await next.fetch('/ssg-fallback-false/first')expect(res.status).toBe(200)
[backport] Update middleware request header (#77202)
packages/next/src/server/lib/router-server.ts+6 −0
@@ -149,6 +149,12 @@ export async function initialize(opts: {renderServer.instance =require('./render-server') as typeof import('./render-server')+const randomBytes = new Uint8Array(8)+crypto.getRandomValues(randomBytes)+const middlewareSubrequestId = Buffer.from(randomBytes).toString('hex')+;(globalThis as any)[Symbol.for('@next/middleware-subrequest-id')] =+middlewareSubrequestId+const requestHandlerImpl: WorkerRequestHandler = async (req, res) => {// internal headers should not be honored by the request handlerif (!process.env.NEXT_PRIVATE_TEST_HEADERS) {
packages/next/src/server/lib/server-ipc/utils.ts+11 −0
@@ -57,5 +57,16 @@ export const filterInternalHeaders = (if (INTERNAL_HEADERS.includes(header)) {delete headers[header]}++// If this request didn't origin from this session we filter+// out the "x-middleware-subrequest" header so we don't skip+// middleware incorrectly+if (+header === 'x-middleware-subrequest' &&+headers['x-middleware-subrequest-id'] !==+(globalThis as any)[Symbol.for('@next/middleware-subrequest-id')]+) {+delete headers['x-middleware-subrequest']+}}}
packages/next/src/server/web/sandbox/context.ts+4 −0
@@ -365,6 +365,10 @@ Learn More: https://nextjs.org/docs/messages/edge-dynamic-code-evaluation`),store.headers.get('x-middleware-subrequest') ?? '')}+init.headers.set(+'x-middleware-subrequest-id',+(globalThis as any)[Symbol.for('@next/middleware-subrequest-id')]+)const prevs =init.headers.get(`x-middleware-subrequest`)?.split(':') || []
test/e2e/middleware-general/test/index.test.ts+13 −0
@@ -102,6 +102,19 @@ describe('Middleware Runtime', () => {}function runTests({ i18n }: { i18n?: boolean }) {+it('should filter request header properly', async () => {+const res = await next.fetch('/redirect-to-somewhere', {+headers: {+'x-middleware-subrequest':+'middleware:middleware:middleware:middleware:middleware',+},+redirect: 'manual',+})++expect(res.status).toBe(307)+expect(res.headers.get('location')).toContain('/somewhere')+})+it('should handle 404 on fallback: false route correctly', async () => {const res = await next.fetch('/ssg-fallback-false/first')expect(res.status).toBe(200)
References
- WEBhttps://github.com/vercel/next.js/security/advisories/GHSA-f82v-jwr5-mffw
- ADVISORYhttps://nvd.nist.gov/vuln/detail/CVE-2025-29927
- WEBhttps://github.com/vercel/next.js/commit/52a078da3884efe6501613c7834a3d02a91676d2
- WEBhttps://github.com/vercel/next.js/commit/5fd3ae8f8542677c6294f32d18022731eab6fe48
- PACKAGEhttps://github.com/vercel/next.js
- WEBhttps://github.com/vercel/next.js/releases/tag/v12.3.5
- WEBhttps://github.com/vercel/next.js/releases/tag/v13.5.9
- WEBhttps://security.netapp.com/advisory/ntap-20250328-0002
- WEBhttps://vercel.com/changelog/vercel-firewall-proactively-protects-against-vulnerability-with-middleware
- WEBhttp://www.openwall.com/lists/oss-security/2025/03/23/3
- WEBhttp://www.openwall.com/lists/oss-security/2025/03/23/4