Closed sshelton76 closed 7 months ago
Even if the underlying issue itself is not that serious, (a) it's still enough to trigger warnings and audit failures, AND (b) the age of the open issue this clearly reveals where security stands as a relative priority for the maintenance and development of this project.
Either one of those are sufficient reason for serious users / those in an enterprise setting to move off of web3 and everything that depends on it.
But that's what happens when there is no incentive for people to do the parts of programming that aren't fun. Fixing bugs isn't fun; going through the bug list isn't fun; but rewriting everything from scratch is fun (because "this time it will be done right", ha ha) and so that's what happens, over and over again.
authors seem to belong to same CADT model
Closing as completed and splitting the history to a new issue doesn't seem to accomplish a whole lot more than getting fewer eyes on updates.
This is not a bug per se, but a request for discussion and if approved as a feature request and if assigned to me, I am volunteering to implement it and submit a PR sometime in the cycle between 1.0 and 2.0.
Here is the deal... https://web3js.readthedocs.io/en/1.0/web3-eth-accounts.html#wallet-save
When a wallet is saved, it presently encrypts it into local storage and you need a password to load the wallet. The problem is that once loaded, the privatekey is available and this leaves the privatekey vulnerable to XSS.
A better solution would be to have 2 passwords, an app level password which is used for marshalling the wallet into and out of storage, and a user supplied password required for signing. The privatekey should remain encrypted until needed.
There are only a handful of times that anyone actually needs the private key. Basically it's only used for signing purposes. So how about we don't store the private key at all?
Instead, we could store half the key, by XORing it against a the bytes derived from a user supplied password "p" stretched into it's own key using a password based key derivation function pbkdf.
We would upgrade the transaction signing logic to require a Uint8Array[32] which would be the bytes from, pbkdf(p). Each time a signing needs to occur, then we supply the user's half of the key, which ideally we should be prompting the user for. Then we just XOR the user supplied part, pbkdf(p), with the stored part to recover the private key, long enough to sign with before disposing of it properly again.
The reason for using typed arrays here is so that they can be zero'd out after each use.
Now keep in mind, there already exist PBKDF and PBKDF2 which are both standards, but are not specifically what I'm talking about. Instead we could either roll our own using argon2 , or just re-use whatever they are doing in ethereumjs-wallet which appears to be 262144 rounds of HMAC-SHA256 according to their README.
That particular function is slow, but could easily be accelerated in the browser using crypto.subtle, the built in crypto library...
There are other things we could do to limit pain points. However I think it goes without saying that our current model has a serious flaw and the only reason it isn't being taken advantage of in the wild, is that it isn't widely known. DApps with their own wallets are presently vulnerable to key leakage if they are storing a wallet in the browser.
If you'd like to see an example of my proposed fix in action, I am taking this direction on one of the projects I'm working on and will be glad to post a link as soon as it's live. This way you can see what the code would look like.
Thank you for taking the time to read this!