Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include headers in JWT issued by callout service (to identify the client/session without exposing it to XSS attacks) #5147

Open
jonaskello opened this issue Feb 29, 2024 · 3 comments
Labels
proposal Enhancement idea or proposal

Comments

@jonaskello
Copy link

jonaskello commented Feb 29, 2024

Proposed change

Currently the proposed way to identify a certain client/session is to include an identifier in the subject. Something like foo.bar.{sessionid}. This pattern works well technically but when used in a web browser this makes the code vulnerable to cross site scripting (XSS) attacks. The way XSS works is that any value available to javascript in the browser can be stolen by an XSS attack. The NATS websocket client in the browser needs to have access to sessionid in order to publish on the subject foo.bar.{sessionid}. By making the sessionId value available to the browser code calling NATS it is also made available for theft by XSS attacks.

In HTTP you can fully block XSS attacks by using a httpOnly cookie. The way it works is that the server after authentication sets a httpOnly cookie with a session identifier. The browser stores this cookie but does not make it available to the javascript running in the browser. So there is no way for malicious actors to steal this cookie value by injecting javascript. The browser will pass this cookie to the server on each request so the server can identify the session. So with httpOnly cookies, the server can set a cookie value that it gets back on every request but no javascript in the browser can see this cookie value, making it fully resistent to XSS attacks.

This is a proposal to include something similar to the HTTP solution in NATS websocket rpc calls by using this flow:

  1. The HTTP server issues a httpOnly cookie containing a sessionId
  2. The NATS websocket server is configured with token_cookie to capture this cookie value and pass it to the auth callout service.
  3. The auth callout service issues a JWT that is associated with the client. This JWT is stored on the NATS server.
  4. The issued JWT can contain a specification of headers that should be added to all calls made by the associated client. For example the callout service can add headers: {session: xxxx, foo: bar} claim to the JWT.
  5. When the client makes a call the NATS server not only validates subjects against the JWT associated with the client, it also appends the headers that are specified in the associated JWT.
  6. Later when a NATS service is called from the web browser, the browser client itself does not add a header with the sessionId. Instead the NATS server adds it, and so the NATS service being called can read the headers and get the sessionId.
  7. The sessionId is never exposed to the client since it stays at the NATS server, and thus there can be no XSS attacks to capture the sessionId in the browser.

The headers that are set by the callout service in 4 does not have to be a session id. Eg. it could be another JWT obtained from an OpenID provider that can then be validated by the NATS services being called. This would be very similar to the bearer token flow commonly used in HTTP where the JWT is passed in the authorization header of each HTTP request.

Related to:

#1173
#4920
#5143

Use case

Web browser client wanting to make NATS rpc calls in a secure way that is not exposed to XSS attacks.

Contribution

No response

@jonaskello jonaskello added the proposal Enhancement idea or proposal label Feb 29, 2024
@aricart
Copy link
Member

aricart commented Feb 29, 2024

The only way that a service can know if a client can or cannot use it is through permissions in the subject - this means that you must clamp your permissions using subjects and operate within that constraint, as I have explained. NATS server by design doesn't track or telegraph client source. if you are worried about cross site scripting then your cors policy is not quite right. - As I suggested in the discussion, you can always implement access to the services from within the HTTP server. This way the HTTP application on the server-side can proxy the request for the client. This simplifies your client as well and allows it to run in a wider range of environments.

@jonaskello
Copy link
Author

The only way that a service can know if a client can or cannot use it is through permissions in the subject - this means that you must clamp your permissions using subjects and operate within that constraint, as I have explained.

Yes, but even if we do this there is no full flow that keeps things things secure (as I explained in the discussion).

NATS server by design doesn't track or telegraph client source.

Yes, currently it does not, which is the problem. Hence the proposal above to make it possible :-)

if you are worried about cross site scripting then your cors policy is not quite right.

CORS policy is unrelated to XSS and cannot prevent it. See for example this answer for more info:

"CORS is unrelated to XSS because any attacker who can place an evil piece of JavaScript into a website can also set up a server that sends correct CORS headers. CORS cannot prevent malicious JavaScript from sending session ids and permlogin cookies back to the attacker. "

@jonaskello
Copy link
Author

In #4920 it is mentioned that NATS will add the header "Nats-Request-Info" if imports are being used. This header will provide details about the connected client. I think this proposal is similar but instead of triggering headers being added by import, they are triggered by specifying an option to add them in the issued JWT.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
proposal Enhancement idea or proposal
Projects
None yet
Development

No branches or pull requests

2 participants