Closed viters closed 12 months ago
const requestHeaders = new Headers(request.headers) // [...] return NextResponse.next({ headers: requestHeaders, request: { headers: requestHeaders, }, })
This code is copying all request headers into server response. It allows the actor to inject malicious headers that will be returned by the server. It also allows modification of other headers, like Cache-Control, which might ex. result in caching malicious responses on CDN.
Thanks for this, sounds bad! 👀
cc @danieltott @gnoff
Can you offer a code example that would resolve the problem while still retaining the functionality of adding CSP (and optionally, a nonce, eg. x-nonce
)?
Further context: copying the Content-Security-Policy
to the request is required for React to read the header there:
// Clone the request headers
const requestHeaders = new Headers(request.headers);
// Set the Content-Security-Policy header in the request for
// App Router routes - this request header will be read in the
// `renderToHTMLOrFlight` function in Next.js and:
//
// 1. The header will be parsed to extract the nonce
// 2. The nonce will be added to the HTML tags which are
// generated by Next.js (eg. the <script /> tags for the
// framework)
//
// This is required in addition to the CSP response header below
//
// Ref: https://github.com/vercel/next.js/blob/29c2e89bd11b644d6665652d3b5c0314fdb0cdc5/packages/next/src/server/app-render/app-render.tsx#L1222-L1227
// Ref: https://github.com/vercel/next.js/issues/43743#issuecomment-1542712188
requestHeaders.set('Content-Security-Policy', cspHeader);
cc @leerob @ijjk I think the part in the docs cloning the request headers to the response headers was introduced in the PR created + reviewed by you:
@karlhorky Thank you for your quick response.
Further context: copying the
Content-Security-Policy
to the request is required for React to read the header there:
Given that, I think its just a matter of handling request and response headers separately:
const requestHeaders = new Headers(request.headers)
requestHeaders.set('x-nonce', nonce)
requestHeaders.set(
'Content-Security-Policy',
// Replace newline characters and spaces
cspHeader.replace(/\s{2,}/g, ' ').trim()
)
const response = NextResponse.next({
request: {
headers: requestHeaders,
},
})
response.headers.set('x-nonce', nonce)
response.headers.set(
'Content-Security-Policy',
// Replace newline characters and spaces
cspHeader.replace(/\s{2,}/g, ' ').trim()
)
return response
I am not sure if thats proper way to propagate response headers, I am not experienced in middlewares.
@leerob in #58300 I think the part of the copying the request headers to the response (allowing reflection attacks) was not removed:
I think the problem here is line 31, no? I'm guessing that this copies the request headers to the newly-created response.
cc @viters
@leerob opened a new PR with this minimal change:
@karlhorky The problem is still present in documentation: https://nextjs.org/docs/pages/building-your-application/configuring/content-security-policy#adding-a-nonce-with-middleware
This closed issue has been automatically locked because it had no new activity for 2 weeks. If you are running into a similar issue, please create a new issue with the steps to reproduce. Thank you.
What is the improvement or update you wish to see?
Official documentation and official examples contain code that is putting the applications using it in considerable danger. It is further escalated due to this code being copied and propagated: google search.
Vulnerable example: https://github.com/vercel/next.js/tree/canary/examples/with-strict-csp
Code snippets and examples should not contain a code that makes the applications using it vulnerable. I have reported this to responsible.disclosure@vercel.com, but was redirected here.
I do not see a reason to set nonce and CSP headers on request headers, so the snippet should just be modifying response headers, without touching request at all.
Is there any context that might help us understand?
This code is copying all request headers into server response. It allows the actor to inject malicious headers that will be returned by the server. It also allows modification of other headers, like Cache-Control, which might ex. result in caching malicious responses on CDN.
Proof of concept
curl -v -o /dev/null -s -H 'Cache-Control: malicious' -H 'X-Malicious: Anyvalue' http://localhost:3000/
Does the docs page already exist? Please link to it.
https://nextjs.org/docs/pages/building-your-application/configuring/content-security-policy