Why Do We Need Refresh Token? Do You Know How to Securely Store Refresh Token and Access Token in the Browser? Part 2

Why Do We Need Refresh Token? Do You Know How to Securely Store Refresh Token and Access Token in the Browser? Part 2

Daily short news for you
  • A software that converts text to speech created by a Vietnamese programmer - J2TEAM - Text to Speech (Free). You can convert dozens of languages into dozens of different natural voices. The special thing is that it is free.

    In preliminary evaluation, the conversion of long texts or texts in pure Vietnamese is very good. However, when it includes English words, it sounds a bit funny 😅

    » Read more
  • How terrifying, Codeium - known as a competitor to Github Copilot, as it allows users to use it for free without limits. Recently, they introduced the Windsurf Editor - no longer just a VSCode Extension but a full Editor now - directly competing with Cursor. And the main point is that it... is completely free 🫣.

    » Read more
  • There is a rather interesting study that I came across: "Users never bother to read things they don't want to." (That's a bold statement, but it's more true than not. 😅)

    Don't believe it? I bet you've encountered situations where you've clicked on a button repeatedly and it doesn't respond, but in reality, it has displayed an error message somewhere. Or you've filled out everything and then when you hit the submit button, it doesn't go through. Frustrated, you scroll up or down to read and find out... oh, it turns out there's an extra step or two you need to take, right?

    It’s not far from the blog here. I thought that anyone who cares about the blog would click on the "Allow notifications" button just below the post. But the truth is, no one bothers to click it. Is it because they don't want to receive notifications? Probably not! I think it's because they just didn’t read that line.

    The evidence is that only when a notification pops up and takes up half the screen, or suddenly appears to grab attention, do they actually read it—and of course, it attracts a few more subscribers—something that was never achieved before.

    » Read more

The Issue

See previous part: Why Do We Need Refresh Token? Do You Know How to Securely Store Refresh Token and Access Token in the Browser?.

In the previous article, we learned about why we should use Refresh Token. The issue at hand is that if the Access Token is stolen, there is a high chance that the Refresh Token will also be compromised. So what's the point of generating a Refresh Token when the possibility of losing both is the same?

To find the answer to this issue, many debates have emerged and it seems that there is no definitive conclusion because everyone has valid points. I play the role of an audience sitting in the stands of a football field, absorbing their opinions and synthesizing them to provide the most satisfactory answer. Of course, it is not entirely accurate and I hope readers can find any inconsistencies in it.

Where to Store Refresh Token?

Where to Store Refresh Token?

My final answer is "in the browser." Depending on the level of security you aim for, you can choose the most suitable option to save time and cost. Inside the browser, there are several storage methods such as Cookies, localStorage, sessionStorage, and even a server-side storage called Session. Each method has its own business logic, and cleverly organizing the storage of Access Token and Refresh Token in it helps minimize the severity of XSS attacks.

A fact is that if your website is attacked by XSS, regardless of where you store them, the chances of Access Token and Refresh Token being leaked are high. To make it easier to understand, let's dissect the nature of an XSS attack. An XSS attack occurs when the attacker can run JavaScript code on your website. This means that the attacker can retrieve the access token stored anywhere the JavaScript code has access to. An XSS attack can occur through third-party JavaScript code in your website, such as React, Vue, jQuery, Google Analytics, etc. The more popular the library, the greater the attention it attracts, and once it is compromised, the damage can be enormous.

It is clear that not using any third-party library in your website is very difficult. Every library added increases the risk. Attackers can also exploit XSS through form/input fields on your website. For example, a public chat feature can be vulnerable to a JavaScript code that steals data. If the display of chat content is not properly handled, other users accessing the website can easily get infected. A MIME sniffing attack can also result in an XSS attack.

Parallel to XSS is a CSRF (Cross-Site Request Forgery) attack targeting the Access Token. It is understood as an attack that forces the user to perform unintended requests. The attacker does not directly steal the Access Token or Refresh Token but takes advantage of the Access Token attached to each request to the server.

For example, a website allows users to change their email through an API:

POST /email/change HTTP/1.1 
Host: yoursite.com 
Content-Type: application/x-www-form-urlencoded 
Cookie: sessionID=qwerty 

[email protected]

The attacker creates an automatic form that sends a POST request to /email/change with their email attached. At that time, the session will be automatically added to the request because you are directly accessing the website, but you are unaware of the presence of that hidden form. However, this can be minimized by using `sameSite`.

From there, it can be concluded that in order to prevent the leakage of Access Token and Refresh Token, they must be stored in a location where JavaScript code has no access. Is there such a place in the browser? Yes, there is, and it is the `httpOnly` cookie. But is httpOnly the ultimate solution? Before answering, I want you to have a clearer understanding of common storage mechanisms in browsers and servers.

Session Cookie

Session is one of the most commonly used ways to identify user sessions in server-side render web applications. The working mechanism is quite easy to understand. The server generates a session ID for each request to identify the session, the browser saves that ID and sends it along with all subsequent requests.

This method requires the server to store and manage sessions for each client. The session ID is typically stored by the client in a Cookie or as a parameter in the URL. This still makes it susceptible to being stolen through an XSS attack.

Cookie

No need to say much, a Cookie is information stored on your computer by a website you visit. It is used to store information you need on a website, such as user login information... Cookies have been one of the oldest methods used for this purpose. The information stored in a Cookie is sent to the server through request headers, allowing the server to identify the user's session.

JavaScript has access to Cookies, so they can still be stolen by an XSS attack.

However, there is a concept called httpOnly for Cookies. If a cookie has the additional httpOnly option specified, it is immune to JavaScript. This means that JavaScript does not have access to the httpOnly cookie, and it is automatically sent with each request to the server.

localStorage and sessionStorage

These are two storage methods with much larger capacity than Cookies. Therefore, they are often used to store large amounts of information or as cache memory for website features to ensure smooth performance.

Information in localStorage is persistently stored until you delete it. On the other hand, sessionStorage is usually cleared after you end your session with the website (close tab, close browser...).

JavaScript has access to both, so they can still be stolen by an XSS attack.

From there, it can be concluded that there are different ways to store:

  • Storing Access Token and Refresh Token in localStorage or cookie -> susceptible to CSRF, stolen through XSS.
  • Storing both in httpOnly cookie -> vulnerable to CSRF, but can be mitigated. Better than the first option.
  • Storing only Refresh Token in httpOnly cookie -> safe from CSRF. Although the Access Token could be stolen, it only exists for a short time, so the risk can be minimized.

It can be seen that the third method is more advantageous than the first two methods. Setting it up is not too complicated. Just when logging in or authorizing, the Access Token is returned in the response body, while the Refresh Token is only returned through the httpOnly cookie.

That's the storage method to minimize the leakage of Access Token and Refresh Token. In case Access Token and Refresh Token are leaked, is there any way to detect or prevent it? I will continue with another article in this series. Thank you for following along.

Premium
Hello

5 profound lessons

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

Every product comes with stories. The success of others is an inspiration for many to follow. 5 lessons learned have changed me forever. How about you? Click now!

View all

Subscribe to receive new article notifications

or
* The summary newsletter is sent every 1-2 weeks, cancel anytime.
Author

Hello, my name is Hoai - a developer who tells stories through writing ✍️ and creating products 🚀. With many years of programming experience, I have contributed to various products that bring value to users at my workplace as well as to myself. My hobbies include reading, writing, and researching... I created this blog with the mission of delivering quality articles to the readers of 2coffee.dev.Follow me through these channels LinkedIn, Facebook, Instagram, Telegram.

Did you find this article helpful?
NoYes

Comments (4)

Leave a comment...
Avatar
Nguyen Danh5 months ago
PA2 - Lưu trữ cả hai trong httpOnly cookie -> dễ bị CSRF nhưng có thể tránh được. PA3 - Chỉ lưu trữ Refresh Token trong httpOnly cookie -> an toàn khỏi CSRF. Vì sao đều lưu ở httpOnly cookie nhưng PA2 thì dễ bị CSRF còn PA3 thì an toàn ???
Reply
Avatar
Trần Ngọc Hải2 years ago
Thật ra ko thể tránh khỏi việc dữ liệu bị đánh cắp cốt là cách xử lý sau khi bị đánh cắp như thế nào cho giảm thiểu tối đa rủi ro
Reply
Avatar
Nguyễn Văn Nhật2 years ago
M có đọc được lời khuyên là nên lưu vào bộ nhớ ứng dụng ví dụ như reduce hoặc một biến nào đó khởi tạo trong ứng dụng. Ví dụ như là const token = 'xxx'
Reply
Avatar
Xuân Hoài Tống2 years ago
Người dùng vẫn có thể đột ngột tắt trình duyệt hoặc hệ điều hành cũng có quyền kết thúc tiến trình thế nên bạn sẽ không kịp lưu lại được
Avatar
Nguyễn Văn Nhật2 years ago
Trước khi reload mình sẽ lưu lại vào localStorage, nó giống như kiểu hiện 1 alert khi bấm vào reload vậy
Avatar
Xuân Hoài Tống2 years ago
Ý bạn là redux? Đúng vậy bạn có thể lưu lại AT hoặc RT vào đó nhưng nếu reload lại page thì sẽ bị mất và người dùng phải đăng nhập lại
Avatar
Long Domi2 years ago
H mới chịu ra cơ mà e muốn hỏi chổ CSRF kia là ntn ạ hơi khó hiểu
Reply
Avatar
Long Domi2 years ago
@gif [tfUW8mhiFk8NlJhgEh] Trả lời bình luận có cả email luôn ghê thật 
Avatar
Long Domi2 years ago
Thanks a e sẽ nghiên cứu ạ
Avatar
Vũ Mạnh Đức2 years ago
CSRF là một cuộc tấn công giả mạo người dùng, bạn có thể đọc thêm trên google hoặc https://developer.mozilla.org/en-US/docs/Glossary/CSRF. Đại loại là kẻ tấn công lợi dụng chính những request từ máy bạn để vượt qua được xác thực (bởi vì các request được gửi từ máy bạn) để thực hiện hành vi yêu cầu quyền xác thực. Những hành vi đó có thể gây nguy hiểm như đổi mật khẩu, đổi email...