Next.js vulnerability which is forcing everyone to upgrade

I have loved working with Next.js and have used it in projects ever since version 10 to 13, and more recently to 14. The release of middleware in version 12 was very exciting. It solved multiple use cases for us, one of them being the redirection to different translations of our website based on locale. Recently, a vulnerability in the very same middleware took the world by storm. An issue surfaced that went back around 3 years ago to version 12 of Next.js. With this post, I aim to quickly summarize the vulnerability along with a demo. The vulnerability The issue was in the code which determines whether the middleware function should run or not. The code was such that if a specific header is passed in with the request, the middleware would be skipped and the request would directly hit the server. The header's name was x-middleware-subrequest. The idea was to use this header as an internal header which will help identify if a middleware has been run before, and based on that prevent the triggering of infinite loops due to recursive requests. This intentional implementation is a vulnerability specifically for apps where authentication logic relies solely on this middleware layer. Since, once a request gets past the middleware there is no further check. Code breakdown In the newer version of Next.js (before the vulnerability was fixed) the code looks like this: While the complete implementation is not something I dived into: It seems like we are trying to limit the recursive depth of Next.js middleware invocation to 5. Once that happens, we will return with the response without running the rest of the logic. It is even more apparent in one of the earlier versions. This is a code snippet from Dec 2021. Here it seems we are simply skipping the middleware logic if the array of subrequests includes the middleware name. We execute the run function with the following parameters where the argument name corresponds to the middleware name. The header So, it seems given the right value of this header we can prevent the logic of middleware from running. In Next.js, middlewares are supposed to be named middleware.ts or middleware.js. Depending on the version of Next.js, they might be placed in different sub-folders but even then a middleware's path can be guessed. That is why this vulnerability is easier to take advantage of. The header x-middleware-subrequest is supposed to be an internal header that is supposed to be set from the middleware and its value be modified at the middleware level itself. But by sending specific values in this header a middleware can be bypassed. The exact value of this header is different for different versions of Next.js. For example: For versions before 12.2: pages/_middleware or pages/dashboard/_middleware based on the route on which the middleware is placed. For versions starting 12.2: middleware For version starting 14: middleware:middleware:middleware:middleware:middleware There are other nuances such as the existence of src folder, but the overall gist is that the value can be guessed in a few attempts. Demo Now Next.js has released patch fixes for the currently supported major versions 14 and 15, but I still tried running this on my dev setup by using an older version. Here is a demo of the same: The above app has 2 pages, a landing page & a protected dashboard page. We check the authenticity of the user at the middleware by checking for a cookie. If the cookie does not exist, we do not let the enter access the dashboard route. If the user tries to visit the dashboard they are redirected to the landing page. This works well. But by passing a custom request header x-middleware-subrequest middleware:middleware:middleware:middleware:middleware, a user can access the dashboard. Learnings There is a lot to learn from this whole incident. Below are some of the key learnings I will take with me: Do not use Next.js middlewares as the sole method for protecting the App. Even Next.js recommends against it. They do suggest using the middleware for optimistic redirecting, for example: missing cookie, but not to rely on it completely. Every security issue should be treated as P0. Next.js team was notified of this issue in Feb, but at the time it seemed to only exist in older versions and that is why it was not acted upon immediately. Later it turned out to be an issue in even the newer versions of Next.js which have middleware. Auth is hard. And for production grade apps, it makes sense to not reinvent the wheel.

Apr 4, 2025 - 20:42
 0
Next.js vulnerability which is forcing everyone to upgrade

I have loved working with Next.js and have used it in projects ever since version 10 to 13, and more recently to 14.

The release of middleware in version 12 was very exciting. It solved multiple use cases for us, one of them being the redirection to different translations of our website based on locale.

Recently, a vulnerability in the very same middleware took the world by storm. An issue surfaced that went back around 3 years ago to version 12 of Next.js.

With this post, I aim to quickly summarize the vulnerability along with a demo.

The vulnerability

The issue was in the code which determines whether the middleware function should run or not. The code was such that if a specific header is passed in with the request, the middleware would be skipped and the request would directly hit the server.

The header's name was x-middleware-subrequest. The idea was to use this header as an internal header which will help identify if a middleware has been run before, and based on that prevent the triggering of infinite loops due to recursive requests.

This intentional implementation is a vulnerability specifically for apps where authentication logic relies solely on this middleware layer. Since, once a request gets past the middleware there is no further check.

Code breakdown

In the newer version of Next.js (before the vulnerability was fixed) the code looks like this:

Image description

While the complete implementation is not something I dived into: It seems like we are trying to limit the recursive depth of Next.js middleware invocation to 5. Once that happens, we will return with the response without running the rest of the logic.

It is even more apparent in one of the earlier versions. This is a code snippet from Dec 2021.

Image description

Here it seems we are simply skipping the middleware logic if the array of subrequests includes the middleware name. We execute the run function with the following parameters where the argument name corresponds to the middleware name.

Image description

The header

So, it seems given the right value of this header we can prevent the logic of middleware from running. In Next.js, middlewares are supposed to be named middleware.ts or middleware.js. Depending on the version of Next.js, they might be placed in different sub-folders but even then a middleware's path can be guessed. That is why this vulnerability is easier to take advantage of.

The header x-middleware-subrequest is supposed to be an internal header that is supposed to be set from the middleware and its value be modified at the middleware level itself. But by sending specific values in this header a middleware can be bypassed.

The exact value of this header is different for different versions of Next.js. For example:

For versions before 12.2: pages/_middleware or pages/dashboard/_middleware based on the route on which the middleware is placed.

For versions starting 12.2: middleware

For version starting 14: middleware:middleware:middleware:middleware:middleware

There are other nuances such as the existence of src folder, but the overall gist is that the value can be guessed in a few attempts.

Demo

Now Next.js has released patch fixes for the currently supported major versions 14 and 15, but I still tried running this on my dev setup by using an older version. Here is a demo of the same:

Image description

The above app has 2 pages, a landing page & a protected dashboard page. We check the authenticity of the user at the middleware by checking for a cookie. If the cookie does not exist, we do not let the enter access the dashboard route. If the user tries to visit the dashboard they are redirected to the landing page. This works well.

But by passing a custom request header x-middleware-subrequest
middleware:middleware:middleware:middleware:middleware, a user can access the dashboard.

Learnings

There is a lot to learn from this whole incident. Below are some of the key learnings I will take with me:

  1. Do not use Next.js middlewares as the sole method for protecting the App. Even Next.js recommends against it. They do suggest using the middleware for optimistic redirecting, for example: missing cookie, but not to rely on it completely.

  2. Every security issue should be treated as P0. Next.js team was notified of this issue in Feb, but at the time it seemed to only exist in older versions and that is why it was not acted upon immediately. Later it turned out to be an issue in even the newer versions of Next.js which have middleware.

  3. Auth is hard. And for production grade apps, it makes sense to not reinvent the wheel.