Sometimes, you might want to encrypt your data stored in the LocalStorage given that it is easily readable by anyone having sufficient knowledge on where to look. Lets see how we can encrypt localstorage data with ease.
Instead of reinventing the wheel, we are going to use a localStorage wrapper that takes care of checking browser support, supports storing more than just strings in the localStorage and most importantly supports encryption – localstorage-slim.js.
Localstorage-slim has an API similar to the LocalStorage API. Besides encryption, it also allows to set an expiry time (ttl
) on the data in the LocalStorage, if that’s something that you want.
As per Localstorage-slim’s documentation, the encryption that it provides by default is not a true encryption but a mere obfuscation. However it should keep most of the users at bay. Considering that, we’ll show you a live example of the same.
1. Obfuscate Localstorage data
As described earlier, we will first see what localstorage-slim provides by default. So lets go ahead and install it with NPM.
# install localstorage-slim npm install localstorage-slim --save
Okay, so we get 2 options,
- Encrypt all data in localStorage (global encryption)
- Encrypt part of the data (i.e. only a few key-value pairs)
We’ll see how to encrypt/obfuscate all data first. To enable global encryption is pretty simple. You only need to turn on the encrypt
flag as seen below –
import ls from 'localstorage-slim'; // enable global encryption ls.config.encrypt = true; // ls.config.secret = 85; // you can provide a global custom secret // save data in localStorage ls.set('hey', 'Hello World'); // data saved gets obfuscated automatically console.log(localStorage.getItem('hey')); // °··ºk¢º½·¯ // calling get will deobfuscate data automatically ls.get('hey'); // Hello world // you can optionally provide a custom secret ls.set('foo', { bar: "baz" }, { secret: 50 }); // calling get with incorrect/missing secret ls.get('foo'); // °··ºk¢º½·¯ // using correct secret ls.get('foo', { secret: 50 }); // { bar: "baz" } // disable encryption for a particular field ls.set('hello', ['Hey', 'beautiful'], { encrypt: false }); ls.get('hello'); // ['Hey', 'beautiful'];
Now, we just dealt with global encryption/obfuscation which encrypts every item added with localStorage-slim. To encrypt/obfuscate only certain key-value pairs, all you gotta do is pass a 3rd parameter to ls.set(...)
with { encrypt: true }
as shown below –
// encrypt/Obfuscate particular fields (we encrypt "foo") ls.set('foo', { bar: "baz" }, { encrypt: true }); ls.get('foo'); // °··ºk¢º½·¯ ls.get('foo', { decrypt: true }); // { bar: "baz" } // encrypt with a custom secret ls.set('hey', 'how are you', { encrypt: true, secret: 88 }); ls.get('hey'); // "zÀÇÏx¹Ê½xÑÇÍz" ls.get('hey', { decrypt: true }) // zÀÇÏx¹Ê½xÑÇÍz ls.get('hey', { decrypt: true, secret: 40 }); // zÀÇÏx¹Ê½xÑÇÍz ls.get('hey', { decrypt: true, secret: 88 }); // how are you
As seen above, we encrypted/obfuscated the value of “foo” stored in the localstorage. To retrieve the correct value with ls.get(...)
, you must set the 3rd parameter { decrypt: true }
. If you used a custom secret, then the custom secret should also be passed to get the expected value.
Feel free to tinker with this JS fiddle that demonstrates encryption/obfuscation with localstorage-slim
2. Encrypt Localstorage data
Sometimes, you want a real secure encryption, localstorage-slim.js allows you to configure its encryption, decryption functions to provide your custom logic. To do so, update the encrypter()
and decrypter()
functions as shown –
// import ls ...; ls.config.encrypt = true; ls.config.secret = '...'; // set a global secret if you wish /* override encrypter */ ls.config.encrypter = (data, secret) => { // your custom logic to encrypt "data" // ... return encryptedString; }; /* override decrypter */ ls.config.decrypter = (encryptedString, secret) => { // your custom logic to decrypt "data" // ... return decryptedData; };
The encrypter
and decrypter
functions get called internally and perform encryption while setting and decryption while getting data respectively.
Thereafter, you can use ls.set('...')
/ls.get('...')
as you normally would.
/* You can use global encryption */ ls.config.encrypt = true; ls.config.secret = '...'; ls.set('Hi', 'Hello'); // data gets encrypted as per your logic ls.get('Hi'); // original data is returned after decryption /* You can encrypt only certain fields */ ls.config.encrypt = false; // default is false // encrypt data ls.set('Hi', 'Hello', { encrypt: true, secret: '...' }); // decrypts and returns original data ls.get('Hi', { decrypt: true, secret: '...' });
You sure are free to write your own algorithm to encrypt data, however, we’re going to use a ready-made, well established and secure algorithm provided by the popular crypto-js library. CryptoJS provides several ciphers/algorithms like AES, TDES, RC4 and Rabbit. As an example, we will be using the AES algorithm. But you are free to use any.
So lets go ahead and install it…
# install localstorage-slim and crypto-js npm install localstorage-slim --save npm install crypto-js --save
Next, we need to update localstorage-slim’s config to make it use the encryption provided by cryptoJS.
import ls from 'localstorage-slim'; import encUTF8 from 'crypto-js/enc-utf8'; import AES from 'crypto-js/aes'; // import tripleDES from 'crypto-js/tripledes'; // to use TDES, replace AES by tripleDES // import RC4 from 'crypto-js/rc4'; // to use RC4, replace AES by RC4 // import rabbit from 'crypto-js/rabbit'; // to use rabbit, replace AES by rabbit // update localstorage-slim ls.config.encrypt = true; // global encryption ls.config.secret = 'secret-string'; // global secret // update encrypter to use AES encryption ls.config.encrypter = (data, secret) => AES.encrypt(JSON.stringify(data), secret).toString(); // update decrypter to decrypt AES-encrypted data ls.config.decrypter = (data, secret) => { try { return JSON.parse(AES.decrypt(data, secret).toString(encUTF8)); } catch (e) { // incorrect/missing secret, return the encrypted data instead return data; } }; // use ls.set() to encrypt data ls.set('number', 25865); // "U2FsdGVkX1/eFb+LVJ4rpM7NN4KiS4ComYEhS4NvF3s=" ls.set('text', 'hello world'); // "U2FsdGVkX1/rYEw5ozWDDBDwsrbMnFkCiUMX7ZCmYyM=" const myObject = { foo: 'bar', krypton: { clark: 'kent' } } ls.set('object', myObject); // "U2FsdGVkX19dt+N13QlAiCBbfiFSyH92lt84xn16Y4gsvFDozGESNhhMvNcX1EiHC3/3YXYWujygFvwT9bFkpA==" // use ls.get() to decrypt data ls.get('number'); // 25865 ls.get('text'); // hello world ls.get('object'); // { foo: 'bar', krypton: { clark: 'kent' } }; // encrypt data with a different secret ls.set('foo', 'bar', { secret: 'new-password' }); // "U2FsdGVkX1+LlKYCJ+uPdJoyWQQKwrbGd4mwk15zdkk=" ls.get('foo'); // "U2FsdGVkX1+LlKYCJ+uPdJoyWQQKwrbGd4mwk15zdkk=" ls.get('foo', { secret: 'new-password' }); // bar
The above example shows how you could setup global encryption (i.e. encrypt all items in the localstorage). Many a times, that is not desired and you want to encrypt only certain key-value pairs. The code snippet below shows how to do it –
/* we use the same "ls.config" as defined above (encrypter, decrypter,..) There is only 1 change, we disable global encryption as seen below */ ls.config.encrypt = false; // default is false ls.config.secret = 'secret-word'; // global secret ls.set('hi', 'hello'); // not encrypted ls.set('hey', 'Bonjour', { encrypt: true }); // encrypted using global secret ls.get('hey'); // "U2FsdGVkX1+Xz0Walh44lamoxF/rsJD51HiZXh0iiFU=" ls.get('hey', { decrypt: true }); // Bonjour (decrypts using global secret) // if you wish to use a different secret than the global secret ls.set('hey', 'Bonjour', { encrypt: true, secret: 'my-password' }); // encrypted ls.get('hey'); // "U2FsdGVkX18r43fSTa2JgCAUPbzR5i4+CT9rcb2sTcg=" ls.get('hey', { decrypt: true }); // "U2FsdGVkX18r43fS5i4+CT9rcb2sTcg=" (tries to decrypt using global secret, but fails) ls.get('hey', { decrypt: true, secret: 'my-password' }); // Bonjour
That’s about it! Your data will be secured successfully within LocalStorage. Here’s a JS fiddle to demonstrate the working of the same.
Note:
If you’re using a global secret for encryption, ensure to not expose “ls” to the window
object as there could be a risk for exposing your secret
. It is therefore recommended to only encrypt data that really needs to be encrypted (i.e. not use global encryption if “ls” is exposed to the window object).
Bonus
Do you wish to set a ttl (an expiry time) on your encrypted data ? Well, then that’s pretty simple with localstorage-slim. All you need to do is to provide an additional ttl
option expressed in seconds as seen below
// encrypts and sets ttl to 5 secs ls.set('hey', 'Bonjour', { encrypt: true, ttl: 5 }); // within 5 secs ls.get('hey', { decrypt: true }); // Bonjour // after 5 secs ls.get('hey', { decrypt: true }); // null
The data expires after 5 seconds and is removed from the localStorage too.
Hope this helps 🙂
References
- https://idkblogs.com/js/304/Secure-your-app-data-in-localStorage
- https://rupesh94.medium.com/how-to-encrypt-localstorage-data-in-angular-270ebcbc1435
- https://github.com/softvar/secure-ls/https://www.npmjs.com/package/encrypt-storage
- https://www.npmjs.com/package/secure-ls
- https://medium.com/@mailshine/cryptojs-secure-storage-data-4d41fed97907