Contract Safety and Security Checklist

This page describes the steps King of the Ether has taken to mitigate against potential contract vulnerabilities.

Ad: Why not check out our new project: UbiTok.io - the unstoppable exchange for trading Ethereum tokens!

C1. Logic Bugs

Simple programming mistakes can cause the contract to behave differently to its stated rules, especially on 'edge cases'.

We have mitigated against this risk by:

Note that we have chosen not to include a mechanism for fixing bugs during the life of the contract due to concern that this mechanism would itself be a serious vulnerability.

C2. Failed Sends

An earlier version of King of the Ether suffered from failure to send compensation payments to wallet contracts created by older versions of the Mist Ethereum Wallet.

We have mitigated against this risk by:

C3. Recursive Calls

Famously, the original DAO contract exhibited unintended behaviour where a "recursive split" technique was used to move Ether worth over US$ 100 Million out of the DAO. This was possible because when a contract sends payment to another contract, the receiving contract's fallback function can call back into the sending contract, which can often produce behaviour the developer of the sending contract developer had not anticipated.

We have mitigated against this risk by:

C4. Integer Arithmetic Overflow

Numbers in Solidity code silently "wrap-around" if they become too large. This can lead to surprising behaviour - e.g. a check like "if (amountOne + amountTwo < myBalance) {...}" can appear to be true if one of the amounts is large enough to cause over-flow.

We have mitigated against this risk by:

C5. Poison Data

Contracts that accept user input that is stored or exposed to other users are vulnerable to being supplied with unanticipated input that causes problems for the contract or for other users of the contract.

We have mitigated against this risk by:

C6. Exposed Functions

It is easy to accidentally expose a contract function which was meant to be internal, or to omit protection on a function which was meant to be called only by priviledged accounts (e.g. by the creator).

We have mitigated against this risk by:

C7. Exposed Secrets

All code and data on the blockchain is visible by anyone, even if not marked as "public" in Solidity. Contracts that attempt to rely on keeping keys or behaviour secret are in for a surpsise.

We have mitigated against this risk by:

C8. Denial of Service / Dust Spam

An attacker may cause inconvenience for other users by supplying the contract with data that is expensive to process, or by repeatedly carrying out actions that prevent others from interacting with the contract.

We have mitigated against this risk by:

C9. Miner Vulnerabilities

Even without serious collusion, Ethereum miners have some limited ability to influence block timestamps and which transactions are chosen in a block (and hence block hashes). A miner or group of miners who control a majority of hashing power in the network can make almost any change they want to contract data or behaviour.

We have mitigated against this risk by:

C10. Malicious Creator

If a contract gives the creator/owner of the contract too much power, they may take funds that should be owned by users of the contract, or change the behaviour of the contract in their favour.

We have mitigated against this risk by:

C11. Off-chain Safety

Rather than attack the contract itself, an attacker may trick users into interacting with a different contract or into sending funds to her address instead of the real contract. This could be done by phishing, by taking control of the website hosting, or by attacking github or social media accounts. An attacker may also attempt to steal Ethereum account private keys from users (or from the contract administrators). For some contracts, accidental loss of private keys belonging to a user important to the contract's contuining operation (e.g. the creator) could prevent operation of the contract.

We have mitigated against this risk by:

C12. Cross-chain Replay Attacks

Following the Ethereum hard-fork, activity has also continued on the Ethereum Classic Chain. Transactions on one chain can be replayed on the other.

We have mitigated against this risk by:

C13. Tx.Origin Problem

This is kind of a "confused depty" problem. If a contract relies on Solidity 'tx.origin' to decide who the caller is (e.g. to see if they're allowed to withdraw their funds), there's a danger that a malicious intermediary contract could make calls to the contract on behalf of the user (who presumably thought the malicious intermediary contract would do something else). See vessenes.com - Tx.Origin And Ethereum Oh My! for a better description.

We have mitigated against this risk by:

C14. Solidity Function Signatures and Fallback Data Collisions


This one is a little bit obscure - but it might catch someone out.

Under the hood, when you call a non-constant external function on a Solidity contract you are really just sending a transaction to the contract with some "magic" data. This works because the Solidity compiler has put some special bytecode at the start of the contract which checks the magic data and invokes the correct function.

Solidity contracts can also have a "fallback" function which is called if there is no data present in the transaction, or if the data does not match any of the contract functions. The fallback function can examine the data sent using the msg.data property.

This can lead to problems in two ways:

  1. Phishing - A user might be tricked into calling a function on a contract - e.g. 'sendAllMyTokensTo(address)' - by being told by another user to send some special data to the contract.
  2. Poisoning - There are some items of data that can never be sent to a fallback function, because they clash with a function signature. For example, if my contract has a function whose signature is foo(uint256), then I can never send data starting with 2FBEBD38 to the fallback function.

We have mitigated against this risk by:

However, in hindsight, we think having a fallback function read msg.data is a mistake - it makes things unnecessarily complicated. It is sometimes desirable to allow users to interact with the contract by sending simple transactions to it (e.g. from web wallets) without having to copy-and-paste an enormous ABI or run a native app. Perhaps it is better to provide some off-chain means (e.g. a Javascript converter) to generate the "magic" data needed to invoke the desired Solidity function? There is a risk of encouraging phishing attacks though by encouraging people to send mysterious data to contracts.

C15. Incorrect use of Cryptography

Cryptographic primitives are notoriously difficult to use correctly - see e.g. If you're typing the letters A-E-S into your code you're doing it wrong.

We have mitigated against this risk by:

C16. Gas Limits

It's quite hard to calculate the maximum amount of gas a contract can use - famously Governmental got stuck due to this. To make matters worse, the maximum gas limit on the network can vary over time based on transaction fees.

We have mitigated against this risk by:

Note that we inadvertently deployed our first contract versions to production using a slightly different version of the Solidity compiler than we tested with - this caused gas costs on the real contract to be slightly higher than expected.

C17. Stack Depth Exhaustion

The Ethereum Virtual Machine has a stack depth limit of 1024 - this can cause calls/sends between contracts to unexpectedly fail. An attacker can set things up so that her contract eats up nearly all the stack just before calling the victim contract.

We omitted to test for this risk originally, but luckily the mitigations we put in place for 'C2. Failed Sends' apply here also. However, it's worth pointing out that it's not just explicit send() calls that can fail - any external call between contracts could fail due to this, though luckily the latter will throw an exception.