While I agree with most of your points, there are hacks or workarounds to get past most of the limitations of localStorage and sessionStorage.
At my last job, I was tasked with writing a SPA, but the person in charge of the backend was an absolute idiot who would rather re-use old, inefficient code than write new code, so I ended up using sessionStorage to store a lot of information (we worked with PHI [Protected Health Information], so this was really a terrible workaround in practice).
Since we had to work with IE10, I couldn't implement an indexedDB solution to the problem, and ran into several issues, firstly, the lack of security. In order to address this, I incorporated Crypto.js into the webapp and encrypted everything before storing it and decrypted when retrieving it. The key to access sessionStorage was created anew every time the page was refreshed, and if a key couldn't be read with the stored key, the data would be deleted and re-fetched.
This led to a second problem: some of the data I needed to store was quite large, and encrypting everything meant I had to base64 encode the result in order to stuff it into sessionStorage, which led to a 33% bloat on everything. Sometimes, this would blow out the 5MB limit. So I ended up incorporating a GZIP library to compress the data when storing and then decompress when retrieving.
This led to a third problem: GZIP isn't the fastest algorithm, and this was all being done synchronously, so when compressing/decompressing large data sets, the browser tab would freeze (we had some searches that could return 3-4MB of data). Fortunately, IE10 at least supports Dedicated Web Workers, so I was able to offload the encryption/compression and decompression/decryption to a worker thread. The worker took the data to be encrypted/compressed/decrypted/decompressed and an encryption key, and returned the result.
Due to the sheer amount of data we could end up processing in a short period of time, I also ended up writing a worker thread pool manager so that we could have 2 or more dedicated workers to do that and not cause lag spikes when doing lots of reads/writes to and from sessionStorage.
Eventually, the solution I ended up with was as follows:
Generate a random encryption key every time a user logs in
Store that key in sessionStorage using a different, fixed encryption key (insecure, but only accessible inside our own code)
Whenever a request that can be cached finishes, send the data off to a worker thread to be compressed, encrypted, and then compressed again (this yielded the best overall size - one 3.5MB JSON payload was compressed to a little over 220KB once)
There's a lot of security issues in the architecture you described above:
Using crypto in client-side JS
Storing an encryption key on a client
etc.
There are other ways to do this stuff safely, although I don't envy your situation.
In a lot of cases focusing on security isn't possible: maybe it's due to a very bad team dynamic (like back-end developers not wanting to work with you), maybe it's due to legacy issues -- but whatever it is, it isn't always feasible.
I like to keep things simple and try to focus on security for apps that require it -- and in these cases I just do the most basic straightforward thing possible.
If you ever find yourself using encryption tools manually (especially in JS) you may want to re-evaluate your goals and see if there's something simpler you can do.
I didn't really have a choice. The users wanted the SPA to be fast, my bosses wanted it to be secure, and the backend engineer was unwilling to address his performance issues. I had to compromise a bit.
For further actions, you may consider blocking this person and/or reporting abuse
We're a place where coders share, stay up-to-date and grow their careers.
While I agree with most of your points, there are hacks or workarounds to get past most of the limitations of
localStorage
andsessionStorage
.At my last job, I was tasked with writing a SPA, but the person in charge of the backend was an absolute idiot who would rather re-use old, inefficient code than write new code, so I ended up using
sessionStorage
to store a lot of information (we worked with PHI [Protected Health Information], so this was really a terrible workaround in practice).Since we had to work with IE10, I couldn't implement an
indexedDB
solution to the problem, and ran into several issues, firstly, the lack of security. In order to address this, I incorporated Crypto.js into the webapp and encrypted everything before storing it and decrypted when retrieving it. The key to accesssessionStorage
was created anew every time the page was refreshed, and if a key couldn't be read with the stored key, the data would be deleted and re-fetched.This led to a second problem: some of the data I needed to store was quite large, and encrypting everything meant I had to base64 encode the result in order to stuff it into
sessionStorage
, which led to a 33% bloat on everything. Sometimes, this would blow out the 5MB limit. So I ended up incorporating a GZIP library to compress the data when storing and then decompress when retrieving.This led to a third problem: GZIP isn't the fastest algorithm, and this was all being done synchronously, so when compressing/decompressing large data sets, the browser tab would freeze (we had some searches that could return 3-4MB of data). Fortunately, IE10 at least supports Dedicated Web Workers, so I was able to offload the encryption/compression and decompression/decryption to a worker thread. The worker took the data to be encrypted/compressed/decrypted/decompressed and an encryption key, and returned the result.
Due to the sheer amount of data we could end up processing in a short period of time, I also ended up writing a worker thread pool manager so that we could have 2 or more dedicated workers to do that and not cause lag spikes when doing lots of reads/writes to and from
sessionStorage
.Eventually, the solution I ended up with was as follows:
sessionStorage
using a different, fixed encryption key (insecure, but only accessible inside our own code)sessionStorage
I hope this is a joke
No, that is not a joke. What part of it made you hope that it was a joke? Perhaps I can clarify.
There's a lot of security issues in the architecture you described above:
There are other ways to do this stuff safely, although I don't envy your situation.
In a lot of cases focusing on security isn't possible: maybe it's due to a very bad team dynamic (like back-end developers not wanting to work with you), maybe it's due to legacy issues -- but whatever it is, it isn't always feasible.
I like to keep things simple and try to focus on security for apps that require it -- and in these cases I just do the most basic straightforward thing possible.
If you ever find yourself using encryption tools manually (especially in JS) you may want to re-evaluate your goals and see if there's something simpler you can do.
I didn't really have a choice. The users wanted the SPA to be fast, my bosses wanted it to be secure, and the backend engineer was unwilling to address his performance issues. I had to compromise a bit.