Summary
You're storing potentially sensitive user data in the browser, client-side, and from time to time referencing the data. I would store the information using an authenticated symmetric cipher, which (depending on how your key management works) gives a strong guarantee of confidentiality as well as integrity. You use data by:
- sending the ciphertext to a separate (maybe 3rd-party) web service
- or, sending the ciphertext back to the application server as part of the http protocol exchange
- or, decrypting the ciphertext and reviewing the data in the client browser to make some sort of decision
In the first two scenarios, you never have the requirement to decrypt on the client-side - you're fundamentally relying on the client to store session data reliably. I'll assume you're not in the first scenario, as you would be making a service call to obtain the Plaid data (basically what you're trying to avoid!). I'll also assume the key-management strategy code can't be interfered with (... which is a loaded assumption if you plan to do decryption on the client side... more on this later).
Discussion
I would propose that you create input key-material for each client also, for example, vested in the cookie. (A cookie could be a good choice because the user may have stricter rules around cookie destruction.) Another choice would be to store the session key material on the http server or in the data store. I wouldn't store it with the encrypted session data. The client provides the encrypted session data from local-storage, as well as any input_key_material
it holds, and the server would then derive key material as needed.
You would be using an authenticated symmetric cipher: Authenticated Encryption with Associated Data. Preferably something like X/ChaCha20Poly1305 or AES-GCM will be up to the task, however, you may need to consult any governance or legislative constraint (eg. some standards restrict your choice to AES-GCM and SHA2).
Generally speaking, this cipher follows the definition: AE( key , nonce , plain_text , assoc_data )
, where plain_text
is the data you want encrypted, and assoc_data
is additional clear-text data that won't be encrypted, but must be authenticated.
It is of critical importance that if plain_text
or assoc_data
changes then so too must the nonce
and/ or the key
. If the code results in key + nonce
re-use for two different plain_text
values, the XOR
of the two ciphertexts will be the same as the XOR
of the two plaintexts. This may lead to data leakage, particularly if the format is known.
A common way to guard against nonce re-use is to use a counter if the nonce is smaller than 16-bytes, or use a random value if it is 16-bytes or greater (TODO ref). This will depend on what cipher you use for AE
. In the case of the former, one solution I've used in the past is to produce a client-session-specific prefix of 8-bytes and then grab 4-bytes from the epoch time. The client holds input_key_material
including the 4-byte timer value, while the prefix (as well as the key) are computed on the server as needed, and disposed of immediately after use. The choice of the timer is a relatively safe one, because any adversary will capture packet timing anyway. (Once you've considered all this, you might ask separately about a potential solution.)
Decrypting client-side
The fundamental problem here is delivering code and key to the client securely. If you search on this site, there's plenty of discussion weighing up pros and cons. There is a recent WebCrypto API that will limit you to AES-GCM and SHA2. You'll need a 'good way' to get key material to the browser.
Personally (at time of writing), I don't have strong opinions either way. You use the tools available and accept the risk. I would use the built-in browser API over a 3rd-party Javascript implementation (eg. sodium.js), because I work on the assumption that an implementation within the browser, that can't be surreptitiously replaced by other javascript code, would be less-malleable.
$_SESSION[]
- i'd be inclined to encrypt the data anyway, because you don't know how it may be exploited in some future case that is not apparent now - you could always use an ephemeral key, generated at time of user login and derived from their details, that is discarded when the user logs/ times out - if you use an authenticated symmetric cipher (tag/ hmac) then you will ensure the integrity of the information, ie. not tampered with by a malign user – brynk Jan 22 at 19:56