2020-06-12 03:38:55 -04:00
|
|
|
_section: Security @<security>
|
2020-04-16 22:25:05 -04:00
|
|
|
|
2021-06-04 01:17:56 -04:00
|
|
|
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.
|
|
|
|
|
|
|
|
_subsection: Side-Channel Attacks
|
|
|
|
|
|
|
|
A [Side-Channel Attack](link-wiki-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.
|
|
|
|
|
|
|
|
_heading: Released Data (Strings, Uint8Arrays, Buffers)
|
|
|
|
|
|
|
|
In JavaScript, memory may not be securely allocated, or more
|
|
|
|
importantly securely released.
|
|
|
|
|
|
|
|
[Historically](https://github.com/nodejs/node/issues/4660),
|
|
|
|
``new Buffer(16)`` would re-use old memory that had been
|
2022-02-03 15:25:26 -05:00
|
|
|
released. This would mean that code running later, may have
|
2021-06-04 01:17:56 -04:00
|
|
|
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
|
2022-02-03 15:25:26 -05:00
|
|
|
assist developers inspect the memory contents of JavaScript
|
2021-06-04 01:17:56 -04:00
|
|
|
programs. In these cases, any //private key// or //mnemonic//
|
2022-02-03 15:46:18 -05:00
|
|
|
sitting in memory may be visible to other users on the system,
|
2021-06-04 01:17:56 -04:00
|
|
|
or malicious scripts.
|
|
|
|
|
|
|
|
_heading: 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.
|
|
|
|
|
2022-02-03 15:25:26 -05:00
|
|
|
Advanced timing attacks are very difficult to mitigate in any
|
2021-06-04 01:17:56 -04:00
|
|
|
garbage-collection-based language. Most libraries where this
|
2022-02-03 15:46:18 -05:00
|
|
|
matters will hopefully mitigate this for you as much as possible,
|
2021-06-04 01:17:56 -04:00
|
|
|
but it is still good to be aware of.
|
|
|
|
|
|
|
|
_heading: General Concerns
|
|
|
|
|
|
|
|
- [Cross-Site Scripting](link-wiki-xss)
|
|
|
|
- [Cross-Site Request Forgery](link-wiki-csrf)
|
|
|
|
- [Phishing](link-wiki-phishing)
|
|
|
|
|
|
|
|
|
2020-06-12 03:38:55 -04:00
|
|
|
_subsection: Key Derivation Functions @<security--pbkdf>
|
2020-04-16 22:25:05 -04:00
|
|
|
|
|
|
|
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](link-wiki-scrypt),
|
|
|
|
which is a memory and CPU intensive algorithm which computes
|
2020-07-03 01:44:17 -04:00
|
|
|
a key (fixed-length pseudo-random series of bytes) for a given
|
2020-04-16 22:25:05 -04:00
|
|
|
password.
|
|
|
|
|
|
|
|
|
|
|
|
_heading: 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
|
2020-07-14 14:12:22 +02:00
|
|
|
scale up an attack, the attacker requires additional computers,
|
2020-04-16 22:25:05 -04:00
|
|
|
increasing the cost to [brute-force attack](link-wiki-bruteforce)
|
|
|
|
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.
|
|
|
|
|
|
|
|
_heading: 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
|
2020-07-03 01:44:17 -04:00
|
|
|
is remaining. Additionally, using language like //"decrypting..."// in
|
2022-02-03 15:46:18 -05:00
|
|
|
a progress bar makes a user feel like their time is not being //needlessly//
|
2020-04-16 22:25:05 -04:00
|
|
|
wasted.
|
|
|
|
|
|
|
|
_heading: 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.
|
|
|
|
|
2020-04-19 02:18:20 -04:00
|
|
|
_code: @LANG<javascript>
|
2020-04-16 22:25:05 -04:00
|
|
|
|
2020-04-19 02:18:20 -04:00
|
|
|
// 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
|
|
|
|
}
|
|
|
|
});
|