Security

While security should be a concern for all developers, in the blockchain space developers must be additionally conscious of many areas which can be exploited.

Once a problem has an economic incentives to exploit it, there is a much larger risk and with blockchain apps it can become quite valuable to attack.

In addition to many of the other security issues app developers may have to worry about, there are a few additional vectors that JavaScript developers should be aware of.

Side-Channel Attacks

A Side-Channel Attack occurs when something orthogonal to the implementation of the algorithm used can be exploited to learn more about secure or private information.

Released Data (Strings, Uint8Arrays, Buffers)

In JavaScript, memory may not be securely allocated, or more importantly securely released.

Historically, new Buffer(16) would re-use old memory that had been released. This would mean that code running later, may have access to data that was discarded.

As an example of the dangers, imagine if you had used a Buffer to store a private key, signed data and then returned from the function, allowing the Buffer to be de-allocated. A future function may be able to request a new Buffer, which would still have that left-over private key, which it could then use to steal the funds from that account.

There are also many debugging tools and systems designed to assist developers inspect the memory contents of JavaScript programs. In these cases, any private key or mnemonic sitting in memory may be visible to other users on the system, or malicious scripts.

Timing Attack

Timing attacks allow a malicious user or script to determine private data through analysing how long an operation requires to execute.

In JavaScript, Garbage Collection occurs periodically when the system determines it is required. Each JavaScript implementation is different, with a variety of strategies and and abilities.

Most Garbage Collection requires "stopping the world", or pausing all code being executed while it runs. This adds a large delay to any code that was currently running.

This can be exploited by attackers to "condition cause a delay". They will set up a scenario where the system is on the edge of needing to garbage collect, and call your code with two paths, a simple path and complex path. The simple path won't stir things up enough to cause a garbage collection, while the complex one will. By timing how long the code took to execute, they now know whether garbage collection occured and therefore whether the simple or complex path was taken.

Advanced timing attacks are very difficult to mitigate in any garbage-collection-based language. Most libraries where this matters will hopefully mitigate this for you as much as possible, but it is still good to be aware of.

General Concerns

Key Derivation Functions

This is not specific to Ethereum, but is a useful technique to understand and has some implications on User Experience.

Many people are concerned that encrypting and decrypting an Ethereum wallet is quite slow and can take quite some time. It is important to understand this is intentional and provides much stronger security.

The algorithm usually used for this process is scrypt, which is a memory and CPU intensive algorithm which computes a key (fixed-length pseudo-random series of bytes) for a given password.

Why does it take so long?

The goal is to use as much CPU and memory as possible during this algorithm, so that a single computer can only compute a very small number of results for some fixed amount of time. To scale up an attack, the attacker requires additional computers, increasing the cost to brute-force attack to guess the password.

For example, if a user knows their correct password, this process may take 10 seconds for them to unlock their own wallet and proceed.

But since an attacker does not know the password, they must guess; and each guess also requires 10 seconds. So, if they wish to try guessing 1 million passwords, their computer would be completely tied up for 10 million seconds, or around 115 days.

Without using an algorithm like this, a user would be able to log in instantly, however, 1 million passwords would only take a few seconds to attempt. Even secure passwords would likely be broken within a short period of time. There is no way the algorithm can be faster for a legitimate user without also being faster for an attacker.

Mitigating the User Experience

Rather than reducing the security (see below), a better practice is to make the user feel better about waiting. The Ethers encryption and decryption API allows the developer to incorporate a progress bar, by passing in a progress callback which will be periodically called with a number between 0 and 1 indication percent completion.

In general a progress bar makes the experience feel faster, as well as more comfortable since there is a clear indication how much (relative) time is remaining. Additionally, using language like "decrypting..." in a progress bar makes a user feel like their time is not being needlessly wasted.

Work-Arounds (not recommended)

There are ways to reduce the time required to decrypt an Ethereum JSON Wallet, but please keep in mind that doing so discards nearly all security on that wallet.

The scrypt algorithm is designed to be tuned. The main purpose of this is to increase the difficulty as time goes on and computers get faster, but it can also be tuned down in situations where the security is less important.

// Our wallet object const wallet = Wallet.createRandom(); // The password to encrypt with const password = "password123"; // WARNING: Doing this substantially reduces the security // of the wallet. This is highly NOT recommended. // We override the default scrypt.N value, which is used // to indicate the difficulty to crack this wallet. const json = wallet.encrypt(password, { scrypt: { // The number must be a power of 2 (default: 131072) N: 64 } });