Next.js Server-Side Request Forgery in Server Actions
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 A Server-Side Request Forgery (SSRF) vulnerability was identified in Next.js Server Actions by security researchers at Assetnote. If the `Host` header is modified, and the below conditions are also met, an attacker may be able to make requests that appear to be originating from the Next.js application server itself. #### Prerequisites * Next.js (`<14.1.1`) is running in a self-hosted* manner. * The Next.js application makes use of Server Actions. * The Server Action performs a redirect to a relative path which starts with a `/`. \* Many hosting providers (including Vercel) route requests based on the Host header, so we do not believe that this vulnerability affects any Next.js applications where routing is done in this manner. ### Patches This vulnerability was patched in [#62561](https://github.com/vercel/next.js/pull/62561) and fixed in Next.js `14.1.1`. ### Workarounds There are no official workarounds for this vulnerability. We recommend upgrading to Next.js `14.1.1`. ### Credit Vercel and the Next.js team thank Assetnote for responsibly disclosing this issue to us, and for working with us to verify the fix. Thanks to: Adam Kues - Assetnote Shubham Shah - Assetnote
The fix
fix redirect
packages/next/src/server/app-render/action-handler.ts+5 −1
@@ -179,9 +179,13 @@ async function createRedirectRenderResult(const forwardedHeaders = getForwardedHeaders(req, res)forwardedHeaders.set(RSC_HEADER, '1')-const host = originalHost.valueconst proto =staticGenerationStore.incrementalCache?.requestProtocol || 'https'++// For standalone or the serverful mode, use the internal hostname directly+// other than the headers from the request.+const host = process.env.__NEXT_PRIVATE_HOST || originalHost.value+const fetchUrl = new URL(`${proto}://${host}${basePath}${parsedRedirectUrl.pathname}`)
packages/next/src/server/lib/start-server.ts+1 −0
@@ -246,6 +246,7 @@ export async function startServer(// expose the main port to render workersprocess.env.PORT = port + ''+process.env.__NEXT_PRIVATE_HOST = `${actualHostname}:${port}`// Only load env and config in dev to for logging purposeslet envInfo: string[] | undefined
Improve redirection handling (#62561)
packages/next/src/server/app-render/action-handler.ts+5 −1
@@ -179,9 +179,13 @@ async function createRedirectRenderResult(const forwardedHeaders = getForwardedHeaders(req, res)forwardedHeaders.set(RSC_HEADER, '1')-const host = originalHost.valueconst proto =staticGenerationStore.incrementalCache?.requestProtocol || 'https'++// For standalone or the serverful mode, use the internal hostname directly+// other than the headers from the request.+const host = process.env.__NEXT_PRIVATE_HOST || originalHost.value+const fetchUrl = new URL(`${proto}://${host}${basePath}${parsedRedirectUrl.pathname}`)
packages/next/src/server/lib/start-server.ts+1 −0
@@ -246,6 +246,7 @@ export async function startServer(// expose the main port to render workersprocess.env.PORT = port + ''+process.env.__NEXT_PRIVATE_HOST = `${actualHostname}:${port}`// Only load env and config in dev to for logging purposeslet envInfo: string[] | undefined
References
- WEBhttps://github.com/vercel/next.js/security/advisories/GHSA-fr5h-rqp8-mj6g
- ADVISORYhttps://nvd.nist.gov/vuln/detail/CVE-2024-34351
- WEBhttps://github.com/vercel/next.js/pull/62561
- WEBhttps://github.com/vercel/next.js/commit/8f7a6ca7d21a97bc9f7a1bbe10427b5ad74b9085
- PACKAGEhttps://github.com/vercel/next.js