How to use JWT tokens for secure access?
If you have come to this page, I hope you know the basics of JWT. Below is a quick overview,
On the client-side, the browser presents the user with a challenge, typically a form-based authentication with a username and password screen. The browser then sends the username & password securely (HTTPS) to the server by HTTP POST or via JSON request. The server authenticates the credentials and once it is successful, the server generates JWT Tokens. The server sends the JWT token to the client in the client response.
On the client-side, once the browser receives the response. Further, the client stores JWT token in cookies or javascript variables or local storage.
This article discusses the various approach and their pros & cons.
Local Storage
Anything that is stored in local storage is prone to XSS attack. i.e. if there is an XSS vulnerability in the web pages then malicious websites can fetch data stored in local storage.
JavaScript variable
This is the same as local storage, if there is an XSS vulnerability then a malicious website can use them to get access to the JavaScript variable.
However, it is better than storing JWT in local storage, as these JavaScript variables are short-lived and indeed malicious script will have to do an extra step to find the JavaScript variable that holds JWT.
Cookies
In my view (also mentioned as the preferred approach in other websites) it is better to store JWT in cookies than in local storage or a javascript variable.
When the application server authenticates the user, it can store JWT in a response cookie with “httpOnly” and “secure” flag.
Cookie sent from server to browser in response
Set-Cookie: JWT=xyzheader.abcbody.efgsign; expires=Wednesday, 01-Oct-2018 10:30:00 GMT; HttpOnly;Secure
This will indicate to the browser that the cookie can be used only in “HTTPS” because of the secure flag and it cannot be accessed by any JavaScript (even if the JavaScript is from the same origin) because of the HttpOnly flag.
httpOnly flag is an indication to the browser that this cookie is not accessible in JavaScript. However, the browser will attach this in cookies on all requests sent to the server. As the cookie is not accessible in JavaScript, even if there is an XSS vulnerability a malicious party cannot access the cookie by injecting JavaScript into the site.
Cookie sent from browser to server in request
Cookie: JWT=xyzheader.abcbody.efgsign; othercookie=someid
Although it is not prone to XSS attack, it is prone to XSRF (Cross-Site Request Forgery) attack. XSRF is also mentioned as CSRF. i.e. it is possible that when a malicious site is accessed (directly or via clicking a link in an email) it can submit a post/get request to the application server. When a request is made to the server, the browser will attach the cookies to the request. Hence the malicious site can change the state of the user data in the application without the knowledge of the user.
Use of XSRF Token can avoid XSRF vulnerability. In the initial authentication request, the application server can return an XSRF Token in the header or cookies with a secure flag (without HttpOnly) in addition to the JWT token in the cookies.
Cookie sent from server to browser in response
Set-Cookie: XSRFToken=id12345; expires=Wednesday, 01-Oct-2018 10:30:00 GMT; Secure
Set-Cookie: JWT=xyzheader.abcbody.efgsign; expires=Wednesday, 01-Oct-2018 10:30:00 GMT; HttpOnly;Secure
XSRF token can be accessed via JavaScript in the client browser and stored as hidden or in a JavaScript variable. From then on, any request sent to the server must contain this XSRF token in the “HTTP HEADER” (for example “XSRF_TOKEN: XDRDDFDFD2345”).
Now request sent to the server will contain both JWT and XSRF Token in cookies & HTTP header respectively. JWT and XSRF Token together will address XSS and XSRF vulnerabilities that may arise if used otherwise.
Even if a malicious site tries to make the user click on a link, the browser will only send JWT cookies and may have XSRF_token in the cookies (based on how it was initially fetched). However, the server expects the XSRFToken in the header and not in request cookies. Hence the request will be rejected by the server.
Cookie & HTTP header sent from browser to server in request
Cookie: JWT=xyzheader.abcbody.efgsign; othercookie=someid
XSRF_TOKEN: id12345
IMPORTANT NOTE: Irrespective of how the server sends the XSRF Token in the response, the client must attach XSRF Token in HTTP HEADER (NOT IN COOKIE). The server must only check for the existence of XSRF TOKEN in the HTTP header (not in the cookie header).
Also published on Medium.