Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Smart Contract Security: How to Never Break the Blockchain

DZone's Guide to

Smart Contract Security: How to Never Break the Blockchain

This article will introduce the reader to the concept of smart contracts and how they are an essential tool for the blockchain world.

· Security Zone ·
Free Resource

Do you know who is accessing your valuable data through your APIs? Discover how

This article is featured in the new DZone Guide to Application and Data Security, Volume II. Get your free copy for more insightful articles, industry statistics, and more!

This article will introduce the reader to the concept of smart contracts and how they are an essential tool for the blockchain world. The article will focus on the aspect of security around smart contracts, common pitfalls and how the user can avoid them. Finally we will see examples of security breaches in smart contracts in the past and what are the best development practices to assure security.

The Blockchain

The blockchain is essentially a very big database of transactions, also known as a transaction ledger. There are many variations but they all share some common properties inspired by the original Bitcoin Blockchain.

A blockchain is made up of a series of blocks. Each block holds a number of transactions that have occurred and a hash of the previous block. Thus all blocks are linked and form a chain. All the transactions in a new block are verified by solving a hard cryptographic puzzle called Proof-Of-Work by many computers around the world. These are the so-called miners.

Miners calculate the Proof-Of-Work hash of all transactions, in essence sealing the new block, and then transmit it to the network so that all nodes know a new block has been produced. All miners compete with each other in order to produce the new block first and get it accepted by the rest of the network. The incentive to do so is that the miner who produces a new block also gets a particular amount of the token of the blockchain in his account. This is how new Bitcoin, Ether, and most other cryptocurrencies are created.

Smart Contracts

Smart contracts are considered one of cryptocurrency's most valuable aspects next to the transfer of value. The term was coined by Nick Szaboin 1994 in the context of bringing the practices of contractual law in the design of electronic commerce.

A smart contract is a computerized transaction protocol that executes the terms of a contract. The general objectives are to satisfy common contractual conditions (such as payment terms, liens, confidentiality, and even enforcement), minimize exceptions both malicious and accidental, and minimize the need for trusted intermediaries. Related economic goals include lowering fraud loss, arbitrations and enforcement costs, and other transaction costs.

In practice right now the most mature platform for smart contracts is the Ethereum Blockchain. It's the second biggest blockchain in market cap and is designed to be a platform for the development and deployment of smart contracts. It uses a Turing complete virtual machine called the Ethereum Virtual Machine (EVM) which empowers users of the blockchain to executes arbitrary code in a trust-less environment. The execution of EVM code is guaranteed and deterministic. EVM code execution is quite slow, and each OPCODE has a particular associated cost which the initiator of a transaction that executes the code is going to pay. For details refer to the Ethereum Yellow Paper.

There have been a few programming languages that translate into EVM, including Mutan, LLL, and Serpent. But without doubt the most well-developed and supported language for smart contracts and the EVM is the contract-oriented language Solidity. In Solidity contracts are first-class objects and a lot of the blockchain attributes involved in EVM transactions, such as the block timestamp or the message sender’s address, are exposed to the developer for use in code. From here on in the article, whenever we refer to smart contracts, we will refer to Solidity smart contracts.

pragma solidity ^0.4.0;

contract SimpleStorage {
    uint storedData;

    function set(uint x) {
        uint y = 10;
        storedData = x + y;
    }

    function get() constant returns (uint retVal) {
        return storedData;
}
}

Above is an example smart contract written in Solidity. A contract, when deployed in the blockchain, has an address, much like a normal user account. In addition to that, it has code attached, which is the EVM translation of the contract, and also a persistent data storage. When a node executes transactions related to a contract, variables like  uint storedData reside in Storage. They are the so-called State Variables. Apart from the persistent data storage Solidity also has an ever-expanding memory where variables can be stored. The memory is wiped for each transaction and it always begins with a clean slate. Finally, small local variables can also be stored on the stack.

Authoring Secure Smart Contracts

Smart contracts act as intermediaries between various parties, and in times may hold large amounts of money. Thus, it is only natural that security should be a primary concern. In this section we will sum up a few good rules to write secure smart contracts. Keep in mind that this list is not exhaustive and smart contract security is an evolving topic. Visit the “Further Reading” links on the bottom of the article for more information.

Reentrancy Protection

Contracts that keep any kind of ledger of their user’s assets in their storage are vulnerable to re-entrancy attacks.

Explanation of the Anti-pattern

Take a look at the vulnerable contract below.

contract DiscountPool {
    mapping (address => uint) tokens;

    // get the amount of money corresponding to my number of tokens
    function getMoney() {
        if (msg.sender.send(tokens[msg.sender])) {
        tokens[msg.sender] = 0;
        }
    }


    function sendToken(address receiver, uint amount) {
        if (tokens[msg.sender] < amount) {
throw;
}
tokens[msg.sender] -= amount;
tokens[receiver] += amount;
    }
}

The above would be the most natural way one would write the contract in any other programming language. But unfortunately what comes naturally for most other programming languages poses a very serious threat in Solidity. Picture the following attacking contract:

contract DiscountPoolAttack {

DiscountPool discountPool;
address owner;
    function DiscountPoolAttack(address discountPoolAddress) {
       owner = msg.sender;
           discountPool = DiscountPool(discountPoolAddress);
    }

    // fallback function
function () {
    discountPool.getMoney()
}

   function getMyLoot() {
       if (msg.sender != owner) {
           throw;
       }
       msg.sender.send(this.balance);
   }
}

The nameless function above is what in Solidity is called the fallback function. Whenever there is a call/send to another contract which sends money or there is a call to a non-existing function, the callback function is also called.

What the malicious contract above achieves is that by using the attack() function it starts a recursive re-entrancy call chain which calls the getMoney() function recursively, not hitting the tokens[msg.sender]=0  line until it is too late and the entire DiscountPool contract above is drained of its funds. After the deed is done, the attacker only needs to call getMyLoot() to receive all of his Ether from the contract.

An important thing to note here is that if  send() is used instead of call() then this exploit is not possible since  send()  only forwards enough gas to the callee for a value transfer and if anything else is attempted then it will fail.

Mitigation

The solution here is to utilize the Checks-Effects-Interactions pattern.

    function getMoney() {
        uint mytokens = tokens[msg.sender];
        tokens[msg.sender] = 0;
        if (!msg.sender.send(mytokens)) {
            throw;
        }
    }

Essentially the ledger of the user’s balance is changed before everything and if the sending does not work then the entire transaction is reverted via the use of  throw;.

Example of the Vulnerability in the Wild

This is the most well-known vulnerability in smart contracts because the arguably most famous Ethereum smart contract, The DAO, was hacked because of it. On the 17th of June, an as-of-yet unknown attacker took advantage of this vulnerability in the DAO’s code here and slowly extracted roughly 30% of the total value held in the contract. At the time, that Ether value was estimated to be about 50 million dollars.

Unchecked Sending of Funds

All functions that send money like send() and  call()  should always be checked for success or failure.

Explanation of the Problem

Sending money does not always succeed. The user may run out of gas or the transaction may fail for some other reason. That’s why the result of money transfers should always be checked.

    // Create a new custodian of funds and transfer the funds to him
    function newCustodian(address custodian_address) {
        custodian_address.send(funds);
        custodian = custodian_address;
    }

In the above example, the send() may fail and we will miss it. When this happens our contract will be broken, attributing the custodian title (whatever that may be) to someone who has not actually received the funds.

Mitigation

Always wrap  send() and  call() within if statements.

    // Create a new custodian of funds and transfer the funds to him
    function newCustodian(address custodian_address) {
        if (custodian_address.send(funds)) {
            custodian = custodian_address;
        }
    }

A good thing about the solidity compiler is that it will issue warnings whenever it sees that you have unchecked sends. Solidity warnings are serious and should always be taken care of.

Example of the Vulnerability in the Wild

An older version (0.4.0) of a Game Of Thrones Pyramid contract, the King of the Ether Throne, was disrupted by a non-checked send() that failed here.

if (currentMonarch.etherAddress != wizardAddress) {
    currentMonarch.etherAddress.send(compensation);
} else {
    // When the throne is vacant, the fee accumulates for the wizard.
}

// Usurp the current monarch, replacing them with the new one.
pastMonarchs.push(currentMonarch);
currentMonarch = Monarch(
    msg.sender,
    name,
    valuePaid,
    block.timestamp
);

Newer versions of that contract are not affected.

Using Block Data for Randomness

In the EVM, code execution is deterministic. Thus it is very tricky to attempt to generate random numbers.

Explanation of the Problem

The hash or the timestamp of a block in the future is unpredictable, deterministic, and is going to be the same for everyone when a particular block N is mined. For that reason, some contracts may choose to use this as a source of randomness.

Unfortunately, as it turns out, this can be gamed. In creating new blocks for the blockchain, miners can control which transactions make it into a block and in which order. Thus they can try to manipulate the outcome of such random numbers as shown here and here.

Mitigation

Random numbers on the blockchain remain quite a hard problem. There are, though, some more involved solutions that are harder to game. Two such examples in Ethereum are the Maker Darts and Randao.

They both function in a similar way. Users make timed deposits accompanied by a secret. A random number is generated every so often by the contract using a combination of the user secrets. If participants lie about their secrets they lose their deposits.

Example of Vulnerability in the Wild

One example of a contract generating random numbers by using block data can be seen in this Roulette game.

Unbound Loops

Every block in the Ethereum blockchain has a gas limit. Each operation of the EVM costs a certain amount of gas. If some operation you attempt consumes so much gas as to hit the block gas limit then it will fail and not be included in the block. This can happen in many different ways, but one that all developers should be aware of is iterating over storage variables.

Explanation of the Problem

Imagine we have a contract that keeps a registry of addresses to names and also wants to at some point pay 1 Wei (smallest ether denomination) to all users.

contract UserRegistry {
    address[] users;
    mapping (address => string) addressMap;
    mapping (string => address) nameMap;


    function register(string name) {
        if (sha3(addressMap[msg.sender]) == sha3('')) {
            throw;
        }
        addressMap[msg.sender] = name;
        nameMap[name] = msg.sender;

        users.push(msg.sender);
    }

    function payAllUsers() {
        for (uint i = 0; i < users.length; i++) {
            if (!users[i].send(1)) {
                throw;
            }
        }
    }
}

It all looks fine at first, but if you take a close look at the payAllUsers() function you will see a loop that is only bound by the size of a storage array. That is very dangerous! The more the registry grows, the more gas the function call will be costing. Eventually it will reach the block gas limit and then the contract will become unusable.

As a bonus problem here, any malicious user can also have a contract whose fallback function
throws. If someone does that then our loop will always hit that malicious fallback function when iterating and fail the loop every time.

Mitigation

Never use loops for something that depends purely on a storage variable and that will grow linearly with time. There should either be an upper bound calculated to fit within the maximum gas limit or a way to split the loop into multiple transactions.

Example of Vulnerability in the Wild

A betting dice game called Etherdice was stopped in its tracks due to this vulnerability. It was iterating all of the bets in the same loop and when the number of bets reached the gas limit the contract stopped functioning.

Compiler Bugs

All deployed Solidity contracts are compiled by the Solidity compiler. The compiler may have a bug and a big batch of contracts may suddenly find themselves vulnerable.

Anything that may go wrong, will go wrong. This is a rule to live by in smart contracts.

Mitigation

Always have some form of a backup plan in your contracts. Things that you never expected to happen may go wrong, or your contract may be vulnerable to a bug in the compiler discovered a long time after you deploy.

    function escapeHatch() {
        if (msg.sender == owner && redAlertCheck()) {
            msg.sender.send(this.balance);
        }
    }

A function like the above would allow the maintainer of a contract to cancel everything and get all of the funds out of a vulnerable contract. redAlertCheck() is optional and could be a check for some invariants of you contract depending on its functionality.

Always remember the DAO. If the DAO had an escape hatch all the funds could have been saved.

Example of Vulnerability in the Wild

On November 1, 2016, a compiler bug that could have affected all contracts deployed with Solidity was discovered. Taking advantage of a compiler bug, a malicious attacker could use an overflow bug and rewrite storage variables. The bug was fixed immediately, and we were quite lucky this time because very few contracts were actually affected and had to be redeployed.

Final Words

Ethereum and smart contracts as a platform are both still in their infancy. The tools at our disposal are maturing, and developers are becoming more security-aware. Soon we will also have formal verification of Solidity contracts, proof that parts of your code fulfill a certain mathematical specification.

As a contract developer, you should adhere to the guidelines described here, monitor Ethereum-related media for news regarding vulnerabilities, and always include failsafes such as escape hatches in your code.

Further Reading

More Security Goodness

For more insight on application security, protection strategies against security attacks, and more, get your free copy of the new DZone Guide to Application and Data Security!

If you want to see other articles in the guide, check out:

Start a free self-guided trial to discover how API Security is done through a true Zero Trust approach.

Topics:
blockchain ,security ,smart contracts

Published at DZone with permission of

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}