I am building a front end for an existing API that uses Ethereum private keys to sign data. This works completely fine for other applications sending data to the API, however I cannot find a secure way to persist private keys in the browser. I am questioning to use LocalStorage since the front end will be in Angular and it handles XSS well. however, most post suggest against placing anything secure in LocalStorage.
Its worth noting that these keys are generated client side and never exposed to the server and it does not matter if the client alters or deletes them.
LastPass utilizes a user entered password to decrypt local keys, I am not opposed to implementing this, however I would prefer the user to not enter a password and rely only on secure storage of the keys.
Any advice is greatly appreciated.
Related
I'm working on a Node.js app which requires the user to enter his API keys from a third party service (the service doesn't allow logging in via oauth).
Right now i'm storing these in a .env file, so it needs to be entered on setup.
I'd like te user to be able to set the keys once in the user interface (with a password) and then store them persistenly, so that when quitting the app and restarting again, the keys will still be there. How would i go about this? Do i encrypt the keys and store them in a db? Are there any other methods?
In general this is too much risk both for your users and you as the service provider. If these are valuable api keys that you want to store, your service becomes a good target. As you also noted, this is what oauth was invented for.
If you still decide to do this, you can trade some ux for more security. You should definitely encrypt api keys, but where to store the encryption keys so that it actually makes sense is always the question.
Think about how an attacker might get hold of these api keys (the threats), and that will help come up with adequate protections (mitigations to those threats).
For example, an attacker might access the database directly or offline, like from a backup, or through a compromised db server. For this, standard encryption at rest as provided by your dbms is good, especially if you use something like AWS and KMS to store the encryption keys. So you need encryption at rest, but that's not enough.
An attacker might compromise your app too, so transparent encryption doesn't help. You can for example encrypt your sensitive fields (ie. the api keys) on the app level, with unique keys associated with each user, stored securely in an appropriate service (like a HSM, or in something like Secrets Manager in AWS). This might still allow access to an attacker, but it's getting more difficult, and you get a lot more control and auditability over key access.
And to take it one step further, you cab derive keys from your users passwords with a proper key derivation function (like pbkdf2 or similar), and never store them in a database, only memory. This means logged in users' secrets might still be compromised by an attacker if they have access to server memory and/or network communications on the server's end (after tls is terminated), but secrets of offline users will still be secure, because even you don't have the key to decrypt them. This of course only works if you only need access to their api keys as long as they're present.
Is there any way to store localstorage securely, like encrypting and decrypting the localstorage data. I don't want other users to manipulate the localstorage data.
If that is not possible with localstorage, what are the other ways to store data at client side?
I have seen websql, but that is also get manipulated easily by writing queries in console.
Note: Can you please provide the solution for Angular 2+ !
Contrary to the other answer, you can securely store any value in the client, where by "securely" I mean the value is not known to the client and/or cannot be modified. The storage mechanism can be localStorage, websql or whatever else. The catch is that Javascript code will not be able to read and/or modify such a value either, because obviously Javascript is the client from what you want to protect such data.
If you have a server-side secret (a key), you can use that to encrypt (for confidentiality) and/or sign (for integrity) any data sent to the client. This is how frameworks like Rails handle sessions by default without server-side persistance and still relatively securely.
Note that simply encrypting a cookie on the server will not necessarily authenticate its contents (see authenticated encryption), and also such a cookie would be vulnerable to replay attacks, against which you can use a timestamp or a nonce. You have to care about forward secrecy if you need it. So in short you have to take care of stuff yourself, which is not straightforward, but not impossible either.
If you only sign data but not encrypt it, Javascript may have access to it, but still won't be able to modify.
No there isn't any way to store data in client side which client won't be able to manipulate.
In angular, you can save data in services but that will be cleared if user refreshes the browser.
We have a file upload system that PGP encrypts data in the browser before it is sent to our servers. The files are then viewed by us in a specially designed local program. We decided this was the safest way to store that data. One downside to this we find in our own testing is that we can really upload anything at all, random strings etc., because we can't verify what that data is once encrypted without keeping our private keys on a server that is connected to the internet. It seems to me that this is a pretty big potential flaw, though I can't put my finger on exactly what could be done with it. Aside from sanitizing and checking data before viewing it, what can we do to mitigate the issue? Should we take another route entirely for protecting the data?
Edit: We realized the public key used to encrypt could simply be changed by an attacker and they'd have access to all files encrypted from that point. The current suggestion is to encrypt using hashed user password+salt generated at login time and then storing that key on another server (protected by master password) so that we too can view them. The issue of changing keys seems to be mitigated by this (though keys could plausibly be intercepted while being sent to the secure storage server).
CryptoJS has functions to create HMAC from a message and the secret key.
How can this be secure considering that the secret key must be stored in plain sight in the JavaScript source deployed on the client ?
Anyone can take the key and issue similar requests to the server under the identity of the original client of the API. Isn't "identity" the problem that HMAC is supposed to solve ?
All in all, I do not understand the purpose of HMAC in client side JS since the key can't be kept secret.
Is there a use case to computing HMAC in JavaScript ?
JavaScript now has WebRTC where two clients can communicate peer-to-peer, this would be a scenario where clients can generate and use their own "secret".
There are some cases where client -> server could be usable as well. If your server was "dynamically" serving the JavaScript then it could insert a "secret" based on the clients current session/login. Assuming you are using HTTPS (if not there could be a man in the middle slurping up the "secret") then it's not unreasonable to assume that communication to the server signed with that specific "secret" (even over unsecured HTTP) belongs to only that client.
How can this be secure considering that the secret key must be stored in plain sight in the JavaScript source deployed on the client ?
Each client should get their own key/secret which enables them access to the resources they are supposed to have access to. This is effectively no different than a user knowing their own username and password. Their user/pass combo only allows access to the resources they need. The same should go for the key pair.
Anyone can take the key and issue similar requests to the server under the identity of the original client of the API. Isn't "identity" the problem that HMAC is supposed to solve ?
Yes, of course if someone gets your key and secret they can issue requests as if they came from you. Simply don't give out your secret to others. Having it in JavaScript doesn't matter at all. Sure, the user can see it but unless they take that key and secret and put it somewhere else, it isn't a problem.
I have a system where a user logs in through normal means (username/password, OAuth, OpenID, etc.) and is immediately issued a key/secret for making API calls. The client-side application uses this key/secret to actually do its work. The issuance of this key/secret is done over HTTPS. I wanted to use HMAC for my API since I wanted the user to be able to pre-sign requests to be used in the open. This method enables me to keep HMAC for the usual administrative GUI as well.
I want to use Stanford's implementation of AES here:
http://crypto.stanford.edu/sjcl/
However, it is essentially useless if I use it as they suggest
SJCL is easy to use: simply run sjcl.encrypt("password", "data") to
encrypt data, or sjcl.decrypt("password", "encrypted-data").
As anyone can simply load my site, look at the java script and use the password to decrypt the data the same way I would.
How do i make this solution useful?
With a bit more back-and-forth I think I understand what you want to know better enough to give you an answer. Let me know if this is what you were looking for.
In general, managing symmetric keys is very difficult. There are two primary uses for symmetric ciphers:
Protecting the privacy of stored data for later use by the same person. If you use an online backup service, you really hope that your data is encrypted on the client before being uploaded to the online service -- you do not want them to have access to your data in plaintext. But when you go to decrypt the data in the future, you're the one to supply the key. This means you could store the key on offline media (CD-R, USB memory stick, SD card, little notebook next to the monitor...) or online (browser cookie, OS-supplied keychain of some sort, or standard file...).
Protecting the privacy of data while in transit between two parties. One party encrypts the data and the other party decrypts the data. This is how spies communicate, how TLS provides HTTPS client-to-server security, how SSH provides client-to-server security. In this case, both parties need to agree to the key that will be used, and this can be done with Diffie-Hellman-Merkle Key Exchange or using a public-key encryption algorithm such as RSA to allow one party to encrypt a randomly-generated session key in a way that it can only be decrypted by the other party.
The library you have found looks like it is highly optimized for the first case -- allowing users to encrypt their data for you to store and then retrieve for them later. Because they use a password based key derivation function it is prepared to handle the poor passwords that are available through simply typing on a keyboard -- the function will allow building safer keys out of just what a human can (or will) type on a keyboard.
Of course, it could be used for the second case, but presumably you're using this in a browser that has full TLS support, which can provide for end-to-end security in case you choose to use both client certificates and server certificates.
If you chose to allow the user to store encrypted data through your software, you should definitely make clear where the decryption key is being stored. I could see a use case for having a cookie on the browser store the key -- so that the user does not need to re-type it when they want their data. But if they change machines or browsers, they'll need to know the password so they can again retrieve their data.
However, if a user thinks their data is "secure", perhaps they should re-type their key every time they want to use their data. That way, the user alone is responsible for the security of the key; browser flaws that allow exposure of cookie data -- or locally-running malicious code -- cannot simply read the key off disk.
The downside of all this, of course, is that there isn't an easy answer available: you have to decide between the simplicity of storing passwords or the safety of not storing passwords.