Risks with Local Storage

When Not to Use Local Storage: Risks, Examples, and Secure Alternatives

Meenu Matharu
4 min readSep 17, 2024

--

Local storage is often used to store data on the client side in web applications. However, it has significant security limitations and can lead to vulnerabilities if used improperly. This blog covers scenarios where using local storage is risky, practical code examples that demonstrate these risks, and better alternatives to ensure data security.

Why Local Storage is Risky for Sensitive Data

Local storage allows you to store key-value pairs in the browser, but this data is not encrypted and can be easily accessed or manipulated. Here are the main security concerns:

  • Lack of Security: Data is stored in plain text.
  • Vulnerable to XSS Attacks: If an attacker injects a script, they can access local storage data.
  • No Access Control: Any script on the same domain can access local storage.

Example of Sensitive Data That Should Not Be Stored in Local Storage

  • User IDs
  • Session IDs
  • JSON Web Tokens (JWTs)
  • Personal Information
  • Credit Card Information
  • API Keys

Scenarios Where Local Storage is Not Ideal

1. Storing Sensitive Data

Sensitive data should never be stored in local storage as it is easily accessible and vulnerable to attacks.

Practical Example: Storing JWT in Local Storage

// Storing JWT in local storage (Not Recommended)
localStorage.setItem('authToken', 'your-jwt-token');

// Accessing the JWT
const token = localStorage.getItem('authToken');

Risk: An attacker can execute an XSS attack to steal the JWT and hijack the user session.

Better Solution: Use HttpOnly Cookies

Store the JWT in an HttpOnly cookie that is not accessible via JavaScript:

// On the server-side, set the JWT in an HttpOnly cookie
res.cookie('authToken', 'your-jwt-token', {
httpOnly: true, // Prevent JavaScript access
secure: true, // Use only over HTTPS
sameSite: 'Strict', // CSRF protection
});

2. User Authentication and Session Management

Storing session identifiers or user roles in local storage can lead to session hijacking.

Practical Example: Storing Session ID in Local Storage

// Storing session ID (Not Recommended)
localStorage.setItem('sessionId', 'user-session-id');

Risk: If an attacker injects a script, they can steal the session ID and impersonate the user.

Better Solution: Use Server-Side Sessions

Store session identifiers on the server and use HttpOnly cookies to manage sessions:

// On the server-side, create a session and set the session ID in an HttpOnly cookie
req.session.userId = user.id;
res.cookie('sessionId', 'generated-session-id', {
httpOnly: true,
secure: true,
});

3. Storing Personal Information

Storing personal information in local storage exposes it to theft if the browser is compromised.

Practical Example: Storing Personal Information in Local Storage

// Storing personal information (Not Recommended)
localStorage.setItem('userProfile', JSON.stringify({ name: 'John Doe', address: '123 Main St' }));

Risk: If an attacker gains access to the browser, they can retrieve this data for malicious purposes.

Better Solution: Fetch Data from the Server on Demand

Store personal data on the server and fetch it only when needed:

// Fetch user profile from server securely
fetch('/api/user/profile', {
credentials: 'include', // Include HttpOnly cookie for authentication
})
.then(response => response.json())
.then(data => {
// Use the profile data
});

4. Storing Payment Information

Payment information should never be stored on the client side.

Practical Example: Storing Credit Card Details in Local Storage

// Storing credit card information (Not Recommended)
localStorage.setItem('creditCard', JSON.stringify({ number: '1234-5678-9012-3456', cvv: '123' }));

Risk: An attacker can steal this data and commit financial fraud.

Better Solution: Use Secure Payment Gateways

Use third-party payment gateways like Stripe or PayPal:

// Use Stripe Elements for secure payment processing
const cardElement = elements.create('card');
cardElement.mount('#card-element');

5. Storing API Keys

API keys are used to access third-party services and should not be stored in local storage.

Practical Example: Storing API Key in Local Storage

// Storing API key (Not Recommended)
localStorage.setItem('apiKey', 'your-api-key');

Risk: If exposed, attackers can misuse the API key for unauthorized access.

Better Solution: Store API Keys Server-Side

Use a server-side proxy to make API requests:

// On the server-side, make the API request using the API key
app.get('/api/data', (req, res) => {
// Use the API key securely on the server-side
fetch('https://api.example.com/data?apiKey=' + process.env.API_KEY)
.then(response => response.json())
.then(data => res.json(data));
});

Secure Alternatives to Local Storage

1. HttpOnly Cookies

HttpOnly cookies are not accessible via JavaScript, providing protection against XSS attacks.

Code Example: Setting an HttpOnly Cookie for JWT

// On the server-side
res.cookie('authToken', 'your-jwt-token', {
httpOnly: true,
secure: true,
sameSite: 'Strict',
});

2. Server-Side Storage

Store sensitive data on the server and only send it to the client when necessary.

Code Example: Fetching Data from the Server

// Fetch sensitive data securely
fetch('/api/user/data', {
credentials: 'include', // Include HttpOnly cookie for authentication
})
.then(response => response.json())
.then(data => {
// Use the sensitive data
});

3. Web Cryptography API

If you need to store some sensitive data on the client side, encrypt it using the Web Cryptography API.

Code Example: Encrypting Data with Web Crypto

// Encrypt data before storing in local storage
async function encryptData(data) {
const encoder = new TextEncoder();
const key = await crypto.subtle.generateKey({ name: 'AES-GCM', length: 256 }, true, ['encrypt', 'decrypt']);
const iv = crypto.getRandomValues(new Uint8Array(12));
const encryptedData = await crypto.subtle.encrypt({ name: 'AES-GCM', iv }, key, encoder.encode(data));
localStorage.setItem('encryptedData', encryptedData);
}

4. Content Security Policy (CSP)

Implement a strong Content Security Policy to prevent XSS attacks.

Code Example: Setting a CSP Header

<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'nonce-abc123';">

Conclusion

Local storage should not be used to store sensitive information like user IDs, session tokens, personal information, credit card details, or API keys. Its lack of encryption, vulnerability to XSS attacks, and lack of access control make it a risky place for sensitive data.

Instead, use secure alternatives like HttpOnly cookies for authentication tokens, server-side storage for sensitive information, and secure payment gateways for handling payment details. By using these best practices, you can ensure that your web application is secure and protects user data effectively.

--

--

Meenu Matharu
Meenu Matharu

Written by Meenu Matharu

🚀 Passionate Frontend Developer | Storyteller on a Coding Journey 🌟 Dive deep into the world of frontend technologies like HTML, CSS, JavaScript and React

No responses yet