What can go wrong when you develop a “secure” crypto wallet? How to eliminate typical security mistakes and build a secure app with multilayered data protection against mnemonic leakage and transaction forgery?
Cossack Labs security engineers were involved in improving the security of several large public blockchain ecosystems and their hot non-custodial crypto wallets.
Here we present some of our observations to help developers build secure crypto wallets and avoid crypto wallet vulnerabilities.
First, let’s talk about the risks and threats relating to crypto wallets, then move to design concerns and implementation issues. In terms of financial risks, crypto wallets’ security baseline is “an old good banking app", meaning OWASP ASVS L3, MASVS L2, and PSD2 are good starting points.
- Hot vs cold crypto wallets
- Crypto wallet security risks and threats
- Common crypto wallet application security flaws
- Mobile crypto wallets: Platform trust issues
- Web crypto wallets: Platform trust issues
- Crypto wallets: Cryptographic flaws
- How crypto wallets communicate with decentralized apps
- How to prevent the user from being a single point of failure
- Supply chain risks
- Crypto wallet security: Practical tips
- Crypto wallet security considerations
This blogpost is a part of the “Digital wallet security guides”: Read the articles How to prevent digital wallet fraud, Exploring security vulnerabilities in NFC digital wallets, Digital payments security architecture guide.
1. Hot vs cold crypto wallets: A comprehensive guide #
The difference between custodial and non-custodial blockchain wallets is pretty straightforward, security-wise.
Custodial crypto wallets rely on third parties (backends) to store users’ private keys, requiring greater user trust. Non-custodial crypto wallets are fully controlled by the user, making them responsible for the tokens’ safety. Often, non-custodial crypto wallets are open-sourced to show the users’ trust and security.
Being open-source is a double-edged sword because attackers can easily read implementation details and find flaws.
Crypto wallets of public blockchains (Tezos, XRPL, Cardano, Bitcoin, Ethereum, etc.) don’t “store” any user data except for account keys in non-custodial wallets: all transactions are public and can be viewed on public ledgers.
Cold crypto wallets (offline, hardware, paper wallets) are one of the safest methods for holding crypto currency because they are not connected to the Internet. But for convenience, people prefer hot (online) wallets: mobile apps or web extensions.
Securing hot crypto wallets is a typical exercise in balancing trade offs: security vs. usability.
2. Crypto wallets security risks and threats #
Consider crypto wallets as “gateways” to the blockchain ledgers.
The attack surface is broad, it combines ledger-specific security issues with risks and threats typical of financial application. Additionally, crypto wallets security heavily depends on their implementation: the selected platform (web, mobile) and developers’ coding choices.
Understanding risks & threats on every level helps to find security mistakes in the overlap of cryptography, platform trust, ledger specifics, and a wallet’s exact implementation.
This overlap hides the most interesting issues.
Deanonymization #
On public blockchains, all crypto transactions are public. Thus, linkability between a user’s identity and public address (deanonymization) can result in the disclosure of the user’s personality or IP address. See Deanonymization of clients in Bitcoin P2P network.
Even privacy-centric systems like Zcash or Monero are affected by the existing deanonymization techniques (see details in An empirical analysis of anonymity in ZCash, Privacy and linkability of mining in ZCash and A traceability analysis of Monero’s blockchain) though to a lesser degree. Public blockchains are vulnerable to user deanonymization via transaction graph analysis and IP-address deanonymization via observation nodes connections.
User deanonymization is typically overlooked by crypto wallets developers.
That’s why we recommend educating users about over-the-shoulder attacks (minimize time secrets appearing on a screen) and the risks of adding friends’ accounts to the address book.
Denial-of-service (DoS) #
Non-custodial wallets can become a component of DoS attacks on individual nodes or even the whole blockchains in some rare cases. It happens when cryptocurrency solution lacks the middleware entity (e.g. crypto wallet backend server) that can validate and filter transactions.
In this case, malformed transactions go straight to the blockchain, waste nodes’ resources, and prevent valid transactions from processing.
We help companies to build secure systems and protect their innovations. Read about armoring a non-custodial wallet for the Tezos blockchain.
3. Common crypto wallet application security flaws #
From the appsec perspective, crypto wallets are just applications.
They have similar threat vectors as any other applications, including user phishing, injections, MitM, brute-forcing users’ passwords, replay attacks, reverse engineering, and malicious 3rd party libraries—everything that can be used to steal wallets’ secrets or fake transactions.
The primary purpose of all non-custodial crypto wallets is to store wallets’ secrets and sign transactions.
Thus, user authentication and secure data storage are the most important security controls presented in every wallet.
User authentication #
Local authentication is much more than just setting a password for the wallet app.
Crypto wallets often miss crucial controls around password and authentication flow: password policy, rotation*, defences against password brute-force, additional authentication step before doing sensitive actions, biometrics verification, tying biometric authN to Keychain/Keystore, etc.
Lack of these controls lowers the bar for attackers and every now and then opens up opportunities for mnemonics and credentials leakage.
* Disambiguation: people on the orange website have noticed the obvious wrong way to read this paragraph, which is primarily due to poor wording more than anything else. Many wallets lack the means to rotate leaked passwords, data-at-rest cryptographic keys, etc. Since these inadequacies are more closely intertwined than in a typical application, our initial write up mentions their deficiencies together.
See OWASP ASVS V2: Authentication and OWASP MASVS V4: Authentication and Session Management Requirements.
Local data storage #
As a non-custodial wallet needs to store the mnemonics, seed, and private keys locally, it is crucial to understand how the local storage works and what the common attacks against it are.
Before storing the data, developers should get the platform-specific answers to the following questions:
- What kind of storage should be used to deliver the best security guarantees? Think about browser local storage vs. session storage, Keychain / Keystore vs storing in plist / preferences.
- Is the storage accessible as a file? Is it accessible by other apps?
- Can you validate the authenticity of such storage? Is it possible to “steal” the storage from one wallet and put it in another one without problems?
- Are there any integrity checks? Or anyone can change stored data and the crypto wallet won’t notice anything.
- Does the storage provide encryption (even hardware-backed encryption), or is the data stored in clear text?
- Should application-level encryption be used to encrypt data before putting it in storage? If this is the case, where will the encryption key be stored and how to derive it?
Crypto wallets security require combining novel cryptography with traditional data, application and product security expertise.
Looking for crypto wallet security audit?
4. Mobile crypto wallets: Platform trust issues #
The cryptocurrency wallet security largely depends on the security of its platform—web, mobile, desktop—and tight integration of security controls between the app and platform.
The more platforms the crypto wallet supports, the more related security issues may arise. Keeping the threat model in mind at all times, let’s look into various aspects of mobile platform security.
For example, crypto wallet mobile applications often do not check if the device is trusted: if it is rooted or jailbroken, if it has potentially harmful app or reverse engineering tools installed, etc. Existing mobile malware can be used to steal users’ credentials, mnemonics or private keys from apps’ memory (see Pegasus, remote iOS, and Android spyware).
OWASP MASVS L2 recommends implementing protection against reverse engineering and tampering for financial, payment, and other apps operating highly sensitive data. It also recommends notifying the users that the device is not trusted because they may be not aware of it.
Mobile platforms (iOS, Android) do offer device-level security controls. Requiring users to set device-level passcodes is a simple feature that creates a significant obstacle for an attacker. If the passcode is not installed, anyone can unlock the phone and steal the wallet data or even access the Keychain/Keystore. Crypto wallets should require using the device-level passcode or at least notify the users that it should be set up.
Another good example of mistrusting the platform is encrypting the wallet’s data before putting it to Keychain / Keystore. Thus, even if the phone is under attack and attackers have access to the Keychain / Keystore, the data is encrypted there (see Application level encryption).
While mobile devices’ and OS’ exploits are out of scope for developers, they can implement security protections to “raise the bar” for attackers. For example, limiting app’s functionality on jailbroken / rooted devices, not supporting old devices, using native secure storage, limiting the lifecycle of sensitive data, thus, decreasing the risks of successful attacks and data leakage.
OWASP (MASVS, MSTG) and NIST (SP 800-57, SP 800-63, SSDF, etc.) describe proven industry guidelines and best practices. Apart from them, developers should always look for platform specific recommendations (see our article on React Native application security).
5. Web crypto wallets: Platform trust issues #
We’ve audited crypto wallets done as web extensions that rely entirely on browser security.
Web crypto wallets’ security operates under certain assumptions that stored data is safe and doesn’t leak. But the web extensions (and users) have no notion of runtime code integrity, so whatever is running on the user machine can be modified at any time.
Web malware’s possibilities are unlimited: phishing users by displaying a malicious version of an “import account” or “send transaction” screen or replacing the content of the clipboard to take advantage of the copy-paste actions when the users try to send tokens to their friends.
Thanks to a browser extension sandbox, other extensions, websites, and out-of-browser processes typically can’t access wallet process memory and read sensitive data. See Breaking out of the Chrome WebExtension sandbox.
However, browsers are a target for exploits and 0days that give memory access to attackers.
According to the Google Security blog, ~70% of security bugs that affect browsers are memory issues.
Specter and Meltdown attacks were published in 2018 by the Google Project Zero; they allow reading privileged memory via side-channel attacks.
Although no measures give 100% protection against 0days and yet-unknown exploits, there are security guidelines that help developers decrease the chances of successful exploitation of known bugs.
The Chrome team recommends developers to enable site isolation, use no-sniff content-type headers, and prevent cookies from entering renders’ process memory. OWASP ASVS, WSTG, and Cheat Sheets are also good sources of inspiration for the industry’s best practices.
6. Crypto wallets security: Cryptographic flaws #
Non-custodial wallets perform many cryptographic operations: they encrypt stored wallet’s secrets, sign transactions, and communicate with decentralized apps using secure protocols.
Some cryptographic choices are dictated by the community (thus, BIP39 is often used to generate mnemonics, and XRPL uses XLS-12), but the crypto wallet developers still have plenty of room to fail.
When people hear about cryptocurrency wallets, they assume that developers are experts in “crypto”, which they confuse for “cryptography”.
But typically, crypto wallet developers are not the same people who are working on the blockchain’s cryptographic core.
Usually, they are regular web, mobile, and desktop developers who understand cryptocurrency concepts but are not experts in modern applied cryptography. Thus, their cryptographic code suffers from common design flaws and implementation mistakes.
Using correct cryptographic primitives for a particular use case is a separate skill that requires additional knowledge and experience.
The most common cryptographic issues in crypto wallets are:
Deriving cryptographic keys from the low entropy secrets without any KDF or choosing poor KDF parameters (think using PBKDF2 with 500 rounds to generate encryption key from the user password, instead of argon2, scrypt, or PBKDF2 with 310000 rounds).
Improper handling of errors and exceptions leading to salt, nonce or IV misuse or leakage.
Using unsuitable crypto primitives for a desired purpose (MD5 instead of Argon2DI, AES-OFB instead of AES-GCM, SHA-256 instead of HMAC-SHA256);
Using the unsuitable block cipher modes for data encryption (AES-CBC doesn’t provide integrity checks, prefer using AES-GCM instead).
Making typical cryptographic mistakes: AES-CBC with zero IV, home-brewing own crypto protocols, using math.random instead of crypto.random, using base64 as “encryption”, and so on.
Poor key management (storing encryption keys in plaintext together with data; lack of key rotation, revocation, and expiration; using cryptographic keys for several different purposes).
Poor memory management of secrets (having sensitive data “everywhere” in the application code, not limiting its lifecycle in memory, storage, and on screen).
Storing wallet’s sensitive data in plaintext (well…).
We typically discovered 8-10 purely cryptography-related issues in every crypto wallet security assessment we performed. As many non-custodial crypto wallets are open-source, cryptographic issues become much easier to identify and exploit for a prying eye.
As we do believe that software developers shouldn’t struggle with tradeoffs of writing cryptography themselves, we strongly recommend using community-proven crypto libraries that are designed for developers and work across many platforms, like Themis and libsodium.
Themis is a high-level cryptographic library that fits perfectly for multi-platform apps. Themis is easy-to-use and hard-to-misuse.
A combination of bad cryptographic choices leads to disaster #
Weakness â„–1. Weak storage scheme for wallets’ data #
The most interesting cryptographic issues we’ve seen were a combination of design and implementation mistakes or bad choices.
Let’s see how several minor issues combined could simplify uncovering wallet’s mnemonics—the core secret of all non-custodial crypto wallets.
One crypto wallet stored sensitive data in a file, encrypted but in a very human-friendly format. Each stored field had an understandable name.
The file itself was stored on a user’s machine in a folder, accessible for curious eyes and any other applications in the system.
wallet_mnemonics:
encrypted_data: "4198fbf....aaca6d"
iv: "e84c2e2bb7c...904f6a16bbad9"
salt: "da2111aeab1182...0c30614931"
password_check:
encrypted_data: "12e1a2ba2c...8492e9"
iv: "521a231a21a...a1bb123c86"
salt: "73aac01746d4d...928da60adb4"
The crypto wallet stores the data in a file, encrypted per field.
The encryption scheme was the following:
It means that every data field was encrypted using AES-256-GCM and a unique-per-field derived encryption key. All encryption keys were derived from the same user password using different salts.
The devil is in the details. If the attacker had access to this file, they only needed to decrypt one field: wallet_mnemonics
. The attacker knows the salt and IV of the encrypted data, making brute-force very straightforward.
Human-friendly names really help attackers quickly locate required fields.
Weakness â„–2. Encrypting multiple fields with the same password #
Brute-forcing becomes even faster with the password_check
field. It is a special utility field, which the crypto wallet uses to determine if the user has entered the correct password. If decryption of this field is successful, the user password is correct.
The original (plaintext) value of the password_check
field was “null”. So, developers were actually encrypting “null” with the user’s password, storing it encrypted, then decrypting and comparing if decrypted == null
.
As all fields are encrypted by keys derived from the same password, the attacker can optimize brute-force by first decrypting the password_check
value. The attacker knows salt, IV, and the value of plaintext null. Brute-forcing password_check
will be faster than brute-forcing wallet_mnemonics
due to known and short plaintext.
Successful brute force results in a user password, which the attacker uses to decrypt the wallet_mnemonics
field (already knowing its salt and IV).
Weakness â„–3. Poor KDF choice and weak parameters #
Usually, a strong password-based KDF should protect against brute-forcing, making it very long, as KDFs are designed to be slow. However, this particular crypto wallet used a PBKDF2-HMAC-SHA256 with only 1000 iterations (instead of 310000 iterations currently recommended by NIST and OWASP).
PBKDF2 is well-known to have weaknesses, CPU and GPU optimizations for faster brute force. Tools like John-the-Ripper already provide GPU support for cracking PBKDF2-HMAC-SHA256.
Using so few rounds with an “old-school” PBKDF2 is a bad choice that makes brute-forcing even easier. Which KDF to use?
Weakness â„–4. Allowing low-entropy user passwords #
The next line of defence is the user’s password. Low-entropy passwords, like “Password1” or “Qwerty123”, are quite common. This crypto wallet used the following password rules: 8 chars length, 1 number, 1 capital, and 1 lowercase letter, which seems fine.
However, it hasn’t checked if the password was a low-entropy string (like “Aa11111”), or a dictionary word (“Matrix12”), or was previously leaked. See NIST SP 800-63b.
As a result, it takes less time to brute-force a more straightforward password.
A combination of weaknesses #
In this example, a combination of bad choices could lead—under unfortunate circumstances—to stealing and reversing crypto wallet’s mnemonics. Location of a file, its human-friendly format, IV and salt placed together with the encrypted data, using the “null” field for a check, using encryption keys derived from the same password for every field, selecting PBKDF2 and using too few iterations for it, allowing weak passwords, and, finally, being an open-source wallet.
We provided multiple recommendations for every aspect of this scheme.
7. How crypto wallets communicate with decentralized apps #
The more features crypto wallets have, the larger is their attack surface and the more sophisticated is the threat model.
One of such typical wallet features is interaction with third-party decentralized applications (depending on a blockchain, they are called dApps or xApps). Some crypto wallets allow quickly interacting with a predefined list of dApps and others embed dApps as WebViews.
This communication introduces the following threat vectors:
Communication between the wallet and the dApp. Without proper authentication, data-in-transit encryption, and authorization of transaction data, the attacker may intercept and modify requests—like changing the transaction’s amount or the recipient’s address.
Malicious dApps. Most blockchains have dozens of dApps created by the community, some of them suddenly gain popularity, others become neglected. Even if the dApp is coming from a trusted source, it doesn’t mean that it has no vulnerabilities. Also, nothing prevents attacking users via the dApp that has been modified and becomes malicious afterwards (refer to typical vulnerabilities caused by DApps).
The way dApps are integrated into the wallet. For example, if the crypto wallet is a mobile app, dApps may be opened in a WebView as regular web pages. As a result, they bring all web-related risks: injection, hijacking, and leaks.
To mitigate these threat vectors, we recommend using a strong transport encryption between crypto wallets and dApps (TLS 1.3 or specific protocols, like Beacon for Tezos ecosystem), doing a proper session management and mutual authentication, and watching the integration point.
Most blockchains we’ve audited don’t have anti-spam and anti-abuse measures other software marketplaces have (think AppStore, Google play market, AWS marketplace, etc.). It makes inexperienced users vulnerable to malicious dApps.
We suggest having a “complaint” button, doing periodical inspections of dApps, and having a dedicated customer support channel related to dApps’ behaviour.
8. How to prevent the user from being a single point of failure #
Non-custodial crypto wallets are secure as long as the user keeps it secure. So, along with in-app security efforts, educating users about their responsibilities is a mission of every crypto wallet dev team.
It doesn’t matter how strict an apps’ security controls are, if the users are easily tricked by phishing attacks or simply lose their wallet’s secrets.
Developers should include quick security tips and hints in the app, especially when users interact with critical features (like transferring their tokens to a new address).
Educate the users because often the attackers do not need any technical skills to obtain someone’s sensitive information through “shoulder surfing”, i. e. observing their computer or mobile device screen and keyboard.
Users may know what blockchain is, what is the current currency exchange rate, how to send transactions, but they may not know how it works or what account mnemonics and private keys are and why they need it. Users may be unaware that mnemonics should be treated the same way as credit card CVV.
We suggest treating mnemonics as a “memorized secret” according to NIST SP 800-63 and follow it.
When building new features for the crypto wallet, always consider how the users might misuse and abuse the app.
Educate the users wherever it is possible, as overwise they can be the weakest link in the wallet’s security mechanism.
Need some help with crypto wallets security assessment?
9. Supply chain risks #
Supply chain attacks pose a significant risk to crypto wallets. Supply chain risks are growing together with an encreasing amount of external dependencies used in the app.
Crypto wallets may have numerous dependencies that have access to wallet sensitive data, like cryptographic libraries (think ECC), official libraries for particular protocols or standards used in the blockchain (think BIP39 or BIP32), and handy utility libraries for every possible purpose (think all npm).
We’ve seen crypto wallets with 110 external dependencies. As every dependency has its dependencies, the total amount of dependencies was 1838 (estimated by yarn audit).
If some dependencies are close sourced, the Dependency confusion attacks might be relevant.
A vulnerability in any of these libraries is a potential vulnerability for the whole wallet.
While adding another dependency may seem to save developers’ time, it may not be the best option for security-related features (see NIST SSDF PW.7). Some dependencies have open issues and PRs that might affect their security.
For example, @elliptic has opened unmerged PR to fix EC point decoding, and Stanford JavaScript crypto library has 98 open issues, some marinating there since 2010.
Another example is that the dependency may not have the required security functionality. For example, react-native-fingerprint-scanner doesn’t handle a fallback action on a biometry change, and react-native-webview doesn’t clear WebView cache properly.
We recommend carefully nurturing external dependencies, patches, and forks, updating them regularly, and using automation tools to support the process (NIST SSDF PW.1, PO.3).
Nine circles of dependency hell #
Researching dependencies sometimes opens an unexpected hole to hell.
Let’s take a look at the React Native crypto wallet that works on iOS and Android. It generates random values used for encryption and private keys generation:
const random = generateRandomValues();
This method calls mvayngrib/react-native-crypto (which, by the way, is now deprecated and renamed to tradle/react-native-crypto). mvayngrib/react-native-crypto has a peerDependency react-native-randombytes. react-native-randombytes, in its turn, depends on a quite outdated Stanford Javascript Crypto Library (SJCL).
According to the react-native-randombytes documentation, it supports two ways of generating pseudo random bytes: synchronous and asynchronous calls.
For async generation, the library uses native CPRNGs: SecRandomCopyBytes on iOS and SecureRandom API on Android. For sync generation, the library uses CPRNG from the SJCL library.
People had questioned this choice before. For example, in this issue, one of the React-Native-crypto maintainers, confirmed the sync generation is less secure than the async one. But they plan to update using a more secure sync way of generating random values (this update hasn’t happened yet).
SJCL is one of the oldest JavaScript libraries, initially created in May 2010. SJCL doesn’t have dependencies, that’s a pure implementation of crypto-primitives in javascript. SJCL was initially designed for web browsers before the WebAPI crypto.getRandomValues became available.
Right now, SJCL has many open issues, it wasn’t updated for several years and is surrounded by discussions about the security of its random function: bitwiseshiftleft/sjcl#77, bitwiseshiftleft/sjcl#178, stackoverflow.
SJCL uses random.js to collect entropy from entropy pools, relying on things like “mouse movements” and “keyboard listener”. Of course, mobile devices don’t have a mouse and keyboard in the same way that desktop devices do.
All of the above leads us to think that SJCL is an unsuitable choice for CPRNG for mobile applications. It was not designed to get entropy from mobile devices, doesn’t have secure default settings and wasn’t updated for a while.
We strongly recommend keeping an eye on sources of cryptographically pseudorandom values, as low entropy values lead to deriving weak and predictable cryptographic material, making attackers’ job easier.
10. Crypto wallet security: Practical tips #
The users are trusting a decent amount of their tokens (~ money) to crypto wallets. They expect the same level of security as they get from other financial applications, banking apps, or better.
Some teams, having built widely popular wallets, lack the security and cryptography expertise to understand that their implementation exposes users to problems. Some of the bike-shed security controls are amazing examples of Schneier’s law. It’s not their fault, as their business is different.
Responsible developers should understand when responsibility becomes hard. Perhaps, when wallet adoption reaches some point, choosing to review and improve security should become a priority.
Here are some practical tips for improving the security of crypto wallets:
- Start with understanding risks and threats: crypto wallets’ risk landscape is a combination of blockchain-related and platform-related risks, cryptography, and application security flaws.
- Educate the user. For a non-custodial crypto wallet, the user is the weakest link in the system. Do your best to prevent phishing attacks: add hints, mask fields, show warnings, and ask for the user’s password before taking critical actions.
- Follow the best practices in cryptography: don’t implement your own ciphers and protocols, use strong encryption, and be careful with key management.
- Do not hesitate to use out-of-the-box platform security controls: they allow “raising the bar” for attackers with minimum developer cost.
- Use automated static code analysis tools (SAST) in your CI pipeline.
- Continuously audit all dependencies that you are using and implement the proper fixes before the next release goes public. Use automated dependency management tools in your CI pipeline.
- Focus on massive exploits. While you can cover single cases, massive exploits may destroy the reputation of the crypto wallet and even the cryptocurrency. Cryptocurrency’s reputation equals its cost.
- Follow the secure coding best practices (e.g. OWASP Secure Coding Practices): data minimization, input validation and sanitization, te principle of least privilege, etc.
11. Crypto wallet security considerations #
Cryptocurrency wallet security is a tricky beast. All the cool crypto wallet vulnerabilities and potential issues we’ve found lie at the intersection of multiple flaws: cryptography, access to the local storage, lack of authentication, and lack of input validation. Each is relatively small, but they open unexpected attack vectors when combined.
From the defender’s point of view, the crypto wallet’s attack surface is enormous. But from the attacker’s point of view, it’s not so difficult to combine 3–4 flaws, especially if the crypto wallet’s code is open-sourced.
Therefore, it makes sense to look not only at specific flaws but at their synergies. Preventing security bugs not only with secure coding and suitable crypto primitives but with the right security design, using proven building blocks and correctly integrating them.
Pushing security early and following the secure software development cycle (SSDLC) saves time and budget spent on security and keeps a clean reputation.
Crypto wallet application security failures allow stealing money faster than from vulnerable mobile banking apps. Unlike banks, public blockchains don’t have a massive anti-fraud system or customer support that can revert transactions. Act accordingly.
If you’re struggling with crypto wallet security, you’re not alone, feel free to drop us a line to get some assistance.
This blogpost is a part of the “Digital wallet security guides”: Read the articles How to prevent digital wallet fraud, Exploring security vulnerabilities in NFC digital wallets, Digital payments security architecture guide.