Are you exposing your localStorage data ?

lets encrypt/decrypt our local Storage in Zustand. saving it from potential security issue!

prerequisite : Reactjs and zustand

here we will see how we can decrypt our localStorage data in the zustand.

Zustand : It's state management library like redux or RTK.

first let see how our normal global state look like. consider it as a simple todo project .

import { create } from 'zustand'
import { persist } from 'zustand/middleware'


export const useStore = create(
  persist(
    (set, get) => ({
      tasks: [],
      logout: false,
      createNewTaskFlag: false,

      createNewTask: (task) =>
        set((state) => ({ tasks: [task, ...state.tasks] })),

      updateTaskFlag: (flag) => set({ createNewTaskFlag: flag }),

      updateLogout: (flag) => set({ logout: flag }),

      deleteTask: (task) =>
        set((state) => {
          const newTasks = state.tasks.filter((el) => el.postId !== task.postId)
          return { tasks: newTasks }
        }),

      setTasks: (tasks) => set({ tasks: tasks }),

      clearStorage: () => {
        sessionStorage.removeItem('store1')
      },
    }),
    {
      name: 'store1', // storage name
      storage : localStorage //saving to localStorage
    }
  )
)

i'm hoping that now you are familiar with this code.

lets start our main goal : )

crypto-js : we will use this lib to decrypt and encrypt our data.

Approach

we will use three fxn inside the createJsonStorage from 'zustand/middleware'

  1. setItem : we will encrypt the data and set it to local Storage

  2. getItem : we will get encrypted data from local Storage and decrypt it.

  3. removeItem : (optional) to remove data from local Storag

this encrytion/decryption work like a wrapper.

what's this means : we are able to access our data as normal we used to do.

import { create } from 'zustand'
import { createJSONStorage, persist } from 'zustand/middleware'
import { enc, AES } from 'crypto-js'

const secretKey = process.env.REACT_APP_ENCRYPTION_KEY

const encrypt = (data) => {
  return AES.encrypt(JSON.stringify(data), secretKey).toString()
}

const decrypt = (encryptedData) => {
  const bytes = AES.decrypt(encryptedData, secretKey)
  return JSON.parse(bytes.toString(enc.Utf8))
}

export const useStore = create(
  persist(
    (set, get) => ({
      tasks: [],
      logout: false,
      createNewTaskFlag: false,

      createNewTask: (task) =>
        set((state) => ({ tasks: [task, ...state.tasks] })),

      updateTaskFlag: (flag) => set({ createNewTaskFlag: flag }),

      updateLogout: (flag) => set({ logout: flag }),

      deleteTask: (task) =>
        set((state) => {
          const newTasks = state.tasks.filter((el) => el.postId !== task.postId)
          return { tasks: newTasks }
        }),

      setTasks: (tasks) => set({ tasks: tasks }),

      clearStorage: () => {
        localStorage.removeItem('store1')
      },
    }),
    {
      name: 'store1',
      storage: createJSONStorage(() => ({
        getItem: (key) => { 
        // get data
          const encryptedData = localStorage.getItem(key)
        // decrypt it
          return encryptedData ? decrypt(encryptedData) : null
        },
        setItem: (key, data) => {
       // before storing it first decrypt it
          const encryptedData = encrypt(data)
         // now store it
          localStorage.setItem(key, encryptedData)
        },
        removeItem: (key) => localStorage.removeItem(key),
      })),
    }
  )
)

Now the encryption and decryption is completed. let me show you the result

As you can see that in localstorage there is 'store1' and its value is encryted.

if you like it then please share it with your friends.

Thank you