Maybe im misunderstanding how people are building endpoints these days, but every post about this I see how it can bypass auth.
Wouldn’t this bypass auth only for sites where auth is true/false?
I’ve never worked on a site were auth is a boolean. Auth is always a relative. The middleware is only there to identify the user. Then when querying for objects, you query objects related to that user.
Or if you are serving an admin page you check that the user is an admin.
I honestly find it more astounding that people put an admin security check to check the url of a page and redirect away in a middleware and no security on the views themselves.
Is this form of checking paths in middleware officially from NextJS or did people just get lazy? Seems like the worst way to build auth I could ever dream up across any framework or language.
If a middleware is bypassed all endpoints should return empty responses. In my nextjs apps the middleware is simply a convenience method for the user if they are logged out they get redirected to the login page. But all api endpoints check for the active user and serve objects relative to the user.
What surprises me here is that the client side of the request / response is not considered a cunning, bitter enemy, as it should be. Why is x-middleware-subrequest even accepted in production? Why is x-middleware-rewrite even returned? They are instrumental to the attack, and the client has no business accessing them, ever, in my book.
If these headers are only expected to be available within a trusted zone, and some fronting HTTP server should strip them from incoming requests and outgoing responses, why are they named like regular HTTP headers, and not in some scary, easy-to-filter-way, like x-INTERNAL-ONLY-middleware-something?
To my mind, the server should accept the bare minimum of headers needed to serve the request, and issue the minimum amount of headers to provide a well-formed response, while being completely opaque to the client. Any nifty diagnostics like x-middleware-rewrire belong to the logs; correlate by request ID. Any nifty internal processing tweaks in plain text, like x-middleware-subrequest, are, to my mind, bad architecture. If you need to pass such info between HTTP endpoints internally, use something like a JWT.
The vulnerability can be understood through this code snippet:
const subreq = params.request.headers['x-middleware-subrequest'];
const subrequests = typeof subreq === 'string' ? subreq.split(':') : [];
// ...
for (const middleware of this.middleware || []) {
// ...
if (subrequests.includes(middlewareInfo.name)) {
result = {
response: NextResponse.next(),
waitUntil: Promise.resolve(),
};
continue;
}
}
Pass an x-middleware-subrequest HTTP header with a colon-separated list of middleware names to skip.https://github.com/vercel/next.js/blob/v12.0.7/packages/next...
Does anyone know which versions of Next.js that is supported?
I don't seem to be able to find a promise from Vercel, but https://endoflife.date/nextjs mentions that 15 and 14 gets security support.
The culture of security within FAANG could not be more opposite than the way that vercel handled this. In big tech, this would have been looked at in 48 hours, and across thousands of systems all oncalls would have been paged to do an emergency deploy. Probably within 5 days, almost the whole company would have deployed the patch.
Vercel to me seems like it is run by hype men, and the CEO is certainly technical, but these people are not in the weeds in the way they come off.
This very recent PR updates the docs to basically remove all common (and previously recommended) middleware use-cases, rendering them almost completely useless:
Most don't understand this issue:
Auth middleware is used for _routing_ (e.g. if you're not signed-in, you'll be redirected to the sign-in page).
This just means a 500 is thrown due to the auth() call returning null on the server.
Can someone tl;dr: why there is even logic to bypass middleware in the first-place, I feel like I'm missing something obvious here...
Timeline is interesting
Timeline:
02/27/2025: vulnerability reported to the maintainers (specifying that only versions between 12.0.0 and 12.0.7 were vulnerable, which was our understanding at the time)
03/01/2025: second email sent explaining that all versions were ultimately vulnerable, including the latest stable releases
03/05/2025: initial response received from the Vercel team explaining that versions 12.x were no longer supported/maintained (probably hadn’t read the second email/security advisory template indicating that all were vulnerable)
03/05/2025: another email sent so that the team could quickly take a look at the second email/security advisory template
03/11/2025: another email sent to find out whether or not the new information had been taken into account
03/17/2025: email received from the Vercel team confirming that the information had been taken into account
03/18/2025: email received from the Vercel team: the report had been accepted, and the patch was implemented. Version 15.2.3 was released a few hours later, containing the fix (+backports)
03/21/2025: publication of the security advisory