DEV Community

Cover image for How To Encrypt Localstorage Data In React JS
Udemezue John
Udemezue John

Posted on

How To Encrypt Localstorage Data In React JS

Introduction.

When building React applications, managing client-side data securely is a key consideration.

LocalStorage, a simple way to store data in the browser, can be tempting due to its ease of use.

However, LocalStorage stores data in plain text, which poses a significant security risk—especially when dealing with sensitive information like authentication tokens or user data.

Encrypting LocalStorage data mitigates this risk, ensuring that even if a malicious user gains access to a device or the browser’s storage, the information remains unreadable without the encryption key.

In this guide, I'll walk through how to effectively encrypt LocalStorage data in React using simple yet secure methods.

Why You Shouldn't Store Plain Text in LocalStorage

When using localStorage, the data remains in plain text, and anyone with access to the browser can easily inspect it. In some cases, this can lead to security vulnerabilities, including:

  • Token Theft: If authentication tokens are stored as plain text, attackers can steal them, leading to unauthorized access.
  • Sensitive Information Exposure: Any sensitive user data (such as email, phone numbers, or addresses) can be viewed or manipulated by malicious actors.

Although using HTTPS can secure data transfer, storing the data securely on the client side is equally important. That’s where encryption comes into play.

How Do I Encrypt Localstorage Data In React JS?

When building a React application, you may need to store data locally on the user's browser, often using localStorage.

While this can be very convenient for saving user preferences, tokens, or temporary session data, there’s a significant caveat: localStorage is not secure by default.

Any data saved there is accessible through the browser’s developer tools, which can be risky if it includes sensitive information like authentication tokens, user IDs, or personal details.

To mitigate this risk, encrypting the data before storing it in localStorage is essential.

Let's walk through how to safely implement encryption for your data in a React app.

  • Installing a library to handle encryption
  • Encrypting data before storing it
  • Decrypting data when retrieving it

1.Choosing an Encryption Library.

There are several libraries available for encrypting and decrypting data in JavaScript.

One of the most commonly used is CryptoJS. It’s simple to use and widely trusted.

First, install the library in your React project:

npm install crypto-js
Enter fullscreen mode Exit fullscreen mode

2. Encrypting Data Before Storing It.

Once CryptoJS is installed, I can start encrypting data before storing it in localStorage. Here’s an example:

import CryptoJS from 'crypto-js';

const setEncryptedData = (key, value) => {
  const encryptedData = CryptoJS.AES.encrypt(JSON.stringify(value), 'secret-key').toString();
  localStorage.setItem(key, encryptedData);
};

Enter fullscreen mode Exit fullscreen mode

In this example:

CryptoJS.AES.encrypt encrypts the data using AES (Advanced Encryption Standard).

I convert the value (which could be any data type) to a string using JSON.stringify since AES encryption works on strings.

'secret-key' is a secret passphrase that is used for the encryption and decryption process.

This should ideally be stored securely on the server side or an environment variable, not hardcoded.

3. Decrypting Data on Retrieval.

When retrieving the encrypted data from localStorage, I need to decrypt it back into its original form:

const getDecryptedData = (key) => {
  const encryptedData = localStorage.getItem(key);
  if (!encryptedData) {
    return null;
  }
  const bytes = CryptoJS.AES.decrypt(encryptedData, 'secret-key');
  const decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));
  return decryptedData;
};
Enter fullscreen mode Exit fullscreen mode

In this case: CryptoJS.AES.decrypt decrypts the data using the same key ('secret-key').

I parse the decrypted string back into its original object form with JSON.parse.

4. Handling Errors Gracefully.

There’s always a chance that something could go wrong during encryption or decryption.

For example, if the stored data becomes corrupted or if someone tampers with it, decrypting could fail. Here’s how to handle errors gracefully:

const getDecryptedDataSafely = (key) => {
  try {
    const encryptedData = localStorage.getItem(key);
    if (!encryptedData) return null;

    const bytes = CryptoJS.AES.decrypt(encryptedData, 'secret-key');
    const decryptedData = JSON.parse(bytes.toString(CryptoJS.enc.Utf8));

    return decryptedData;
  } catch (error) {
    console.error("Decryption failed:", error);
    return null;
  }
};
Enter fullscreen mode Exit fullscreen mode

In this version, the try-catch block ensures that if decryption fails, I log the error and return null instead of breaking the application.

5. Storing and Managing Keys.

Hardcoding a secret key in your JavaScript code can expose the application to serious security risks, especially if someone inspects your code. Ideally, encryption keys should be:

  • Environment-based: Use environment variables to store the encryption key securely.
  • Server-generated: Generate the key server-side and store it securely. Send it to the client only when necessary (and ideally over a secure connection).

In a production environment, one option is to use services like AWS Secrets Manager or environment variables (process.env.SECRET_KEY) to inject the secret key during build time.

When Should You Use Encryption for LocalStorage?

Not every piece of data stored in localStorage requires encryption. Encryption is computationally expensive, so it's best reserved for sensitive information. Here’s when you might want to encrypt data:

  • Authentication tokens (JWTs): While best practices encourage storing tokens in HTTP-only cookies, if you must store them in localStorage, encrypt them.
  • User preferences that may reveal sensitive information: For example, if users are saving personal information such as addresses or private data.
  • Sensitive app state: Any data that could be exploited to gain unauthorized access to app features or user accounts.

Additional Considerations.

1. Security Best Practices.

  • HTTPS: Always serve your application over HTTPS to prevent man-in-the-middle attacks.
  • Token Expiry: Use short-lived tokens where possible. Even if tokens are stolen, they should expire quickly.
  • Monitor Vulnerabilities: Encryption libraries may have vulnerabilities, so it's crucial to keep them updated. Services like Snyk can help monitor dependencies for known vulnerabilities.

2. Performance Implications.

Encrypting and decrypting data can add some overhead, especially for large datasets.

Ensure the data you store in localStorage is minimal and necessary. Where possible, offload larger datasets to APIs or secure databases.

Conclusion.

Encrypting data stored in localStorage is a vital step for securing sensitive information in a React application.

Using libraries like CryptoJS makes the process straightforward, ensuring that even if an attacker gains access to the browser's developer tools, they won’t be able to read the data without the secret key.

However, encryption is just one layer of security. Coupled with secure handling of encryption keys, HTTPS, and regular security audits, encryption can be a powerful tool to protect your application and users from potential attacks.

Top comments (0)