Nextjs: Ability to fetch HTTPS-ONLY cookies using server actions, is there a vulnerability?

80 views Asked by At

Since Next 13.4 we are able to do the following

  • Set a cookie with HTTP-ONLY flag
  • Using Server Action in a client component to fetch a HTTP-ONLY cookie. Basically I can have a button that when you click it, it will return the value of the HTTP-ONLY cookie, we attach it to the request headers

Is this secure? The biggest compromise of jwt token storage is to either expose yourself to XSS or CSRF against our jwt

With Nextjs, we're able to use Bearer Auth, and store the cookie in http-only, and attach it to headers, thus reducing both XSS and CSRF attack vectors.

Is there a vulnerability to this?

1

There are 1 answers

7
VonC On

Is there a vulnerability to this?

There should not be a vulnerability to this, as long as the fetched HTTP-only cookie is not exposed to client-side JavaScript. The use of HTTP-only cookies is a security measure to prevent client-side scripts from accessing the cookie data, which helps mitigate cross-site scripting (XSS) attacks.

Basically can I have a button that when you click it, it will return the value of the HTTP-ONLY cookie, we attach it to the request headers?

I suppose, as long as everything is done on the server-side, through a server action. It does not matter if it is through:

  • SSR (Server-Side Rendering), which involves rendering components on the server and sending the HTML to the client. During SSR, HTTP-ONLY cookies can be accessed on the server to pre-render pages with data that may be user-specific.
  • RSC (React Server Components), allowing you to build components that render exclusively on the server. These components can directly access server-side data sources, such as databases or the file system, and can work with HTTP-ONLY cookies on the server.

The button mentioned in your question is likely intended to trigger a server-side action. The button would be part of a client-side component, and when clicked, it would send a request to the server action.

The reason for the button would be to initiate an interaction that requires server-side processing involving HTTP-ONLY cookies, such as refreshing a session, fetching user-specific data, or performing an action that requires authentication. By using server-side logic to handle the cookie, you would keep the token secure by never exposing it to the client-side environment, thus reducing the risk of XSS and CSRF attacks.

A typical workflow would be:

Client-Side                  Next.js Server                 External API
     |                             |                             |
     |     Click Button            |                             |
     |------------------------->   |                             |
     |                             | Read HTTP-only cookie       |
     |                             | from request headers        |
     |                             |                             |
     |                             | Attach cookie to            |
     |                             | Authorization header        |
     |                             |                             |
     |                             |---------------------------> |
     |                             | Make authenticated          |
     |                             | request to API              |
     |                             |                             |
     |  <------------------------- |                             |
Receive |  Response (no sensitive  |                             |
   data |     data exposed)        |                             |
     |                             |                             |
     |                             |                             |
     V                             V                             V

The user interacts with the client-side by clicking a button, and the click event triggers a request to a server-side endpoint in the Next.js application.

The server-side code reads the HTTP-only cookie from the request headers (cookies are automatically sent by the browser with the request). It then attaches this cookie to the Authorization header (as a bearer token) for an outgoing request to an external API. This action is performed server-side and the cookie value is never exposed to the client.

The Next.js server makes (for instance) the authenticated request to the external API. Once the external API responds, the Next.js server processes the response. It may return some data to the client-side, but the HTTP-only cookie value itself is not included in this response, ensuring that it's not exposed to client-side JavaScript.


As a good illustration of this issue, consider vercel/next.js discussion 59303:

No way to (securely) access cookies/headers for client component SSR

The discussion touches on the need for a method to access cookies securely during SSR without exposing them to the client, which is precisely your concern regarding the secure handling of JWTs and other sensitive information stored in cookies.

Passing the cookie as a prop to the client component will result in the cookie value being serialized in the page and thus accessible to JavaScript.

So this is an area where Next.js might need further development to make sure security best practices can be easily and reliably implemented.


But what if the flow is: I am on the client, I get the cookies via server actions, then put it in the request Headers Authorization?
The environment is all on the client side except the cookie fetching. So the attaching cookies part gets moved to the client side

If you have the following workflow:

Client-Side                  Next.js Server                 External API
     |                             |                             |
     |     Click Button            |                             |
     |------------------------->   |                             |
     |                             | Fetch HTTP-only cookie      |
     |                             |                             |
     |  <------------------------- |                             |
Receive | Cookie value (Security   |                             |
cookie  | Issue: Exposed HTTP-only |                             |
value   | cookie)                  |                             |
     |                             |                             |
     | Attach cookie to            |                             |
     | Authorization header        |                             |
     | (Security Issue:            |                             |
     | Client handles sensitive    |                             |
     | cookie directly)            |                             |
     |                             |                             |
     |---------------------------> |                             |
     | Make authenticated          |                             |
     | request to API              |                             |
     | with exposed cookie         |                             |
     |                             |                             |
     |                             |                             |
     V                             V                             V

Then the application has a security flaw, if the client code is able to receive the value of an HTTP-only cookie: HTTP-only cookies should be strictly handled by the server, and any action that requires those cookies should involve a server-side process that does not reveal the cookie's value to the client.

To maintain the security of HTTP-only cookies, the correct approach would be:

  • The client-side code requests a server action without handling or knowing the cookie's value.
  • The server action reads the HTTP-only cookie and makes any necessary secure requests with the proper Authorization headers.
  • The server action then responds to the client-side code with any non-sensitive data required for the client to proceed.

In this secure workflow, at no point does the client-side JavaScript have access to the HTTP-only cookie value.


I do wonder, how would an XSS attack get the cookie when it lands on the client?

In other words, how these attacks (XSS -- Cross-Site Scripting) can exploit vulnerabilities in web applications to execute malicious scripts in the context of a user's session.
The key point about HTTP-only cookies is that they are designed to be inaccessible to JavaScript running in the browser. That means that even if an XSS attack occurs, the attack script should not be able to read or steal HTTP-only cookies directly because the browser will not allow JavaScript to access these cookies.

Browser (Client-Side)                 Attacker's Script
       |                                     |
       |  Executes in the context of         |
       |  the user's session                 |
       |                                     |
       |  Tries to access cookies            |
       |------------------------             |
       |                        \            |
       |                         ---------------------------
       |                         | Cannot access HTTP-only |
       |                         | cookies                 |
       |                         ---------------------------
       |                                      |
       V                                      V

However, your scenario involves moving part of the authentication process to the client-side, specifically fetching a token via server actions and then attaching it to request headers on the client-side.

But: If an HTTP-only cookie can be "fetched" and manipulated by client-side JavaScript (as in, the JavaScript can read the value and attach it to headers), it contradicts the HTTP-only flag's purpose. By definition, an HTTP-only cookie cannot be accessed through JavaScript running in the client's browser.

The concern about XSS in the context you mentioned only comes true if the token (stored in an HTTP-only cookie) is somehow made accessible to client-side scripts. That would usually not be possible without server-side assistance due to the HTTP-only restriction.
If an application design allows sensitive information from HTTP-only cookies to be exposed to the client-side (even if indirectly), it could indeed become vulnerable to XSS, where malicious scripts read and exfiltrate these tokens.

The primary way an XSS attack could "get the cookie" is if the application logic inadvertently exposes the cookie's value to the client-side, allowing JavaScript to read it.
That might happen if:

  • the server-side embeds the token in HTML or JavaScript that is sent to the client.
  • the application uses a less secure method to transmit the token to the client, where it is not properly protected.

In secure design, HTTP-only cookies are strictly handled by the server, and any client-side operations requiring authentication should be designed not to expose sensitive token values.
Properly used, HTTP-only cookies offer robust protection against XSS concerning cookie theft, but application logic must also make sure no sensitive data is exposed through other means to the client-side.


The cookie gets put straight into the subsequent request headers, and nothing else....

HTTP-only cookies are a security feature intended to mitigate the risk of client-side script access to the protected cookie. If a cookie is marked as HTTP-only, it is not accessible through JavaScript APIs such as document.cookie. That restriction is enforced by web browsers to prevent the cookie from being accessed by unauthorized scripts, particularly in the event of an XSS attack.

In your scenario:

  • A cookie is fetched via server actions.
  • That cookie is then directly put into the request headers for subsequent requests, all on the client side.

But: when a server sets an HTTP-only cookie, the browser automatically enforces the HTTP-only policy. That means that no JavaScript running in the browser can read or manipulate this cookie.
So the scenario where client-side JavaScript fetches an HTTP-only cookie, and then manually adds it to the headers of a subsequent request contradicts the enforced behavior of HTTP-only cookies.

Authentication tokens and sensitive information stored in HTTP-only cookies are meant to be handled server-side. The server can read the HTTP-only cookies and include them in request headers when communicating server-to-server or responding to server-side requests. That process is invisible and inaccessible to client-side JavaScript.

Allowing client-side scripts to directly access and manipulate HTTP-only cookies would significantly increase the risk of security vulnerabilities, including XSS attacks. It would enable malicious scripts to steal or manipulate authentication tokens, leading to unauthorized access.

In any secure web application architecture, operations that require access to sensitive cookies, especially those marked as HTTP-only, must be handled by server-side logic.
The client-side code can trigger these operations via requests to the server, but the actual access and manipulation of the cookies should remain within the server's domain.