1.. _security_considerations: 2 3####################### 4Security Considerations 5####################### 6 7While it is usually quite easy to build software that works as expected, 8it is much harder to check that nobody can use it in a way that was **not** anticipated. 9 10In Solidity, this is even more important because you can use smart contracts 11to handle tokens or, possibly, even more valuable things. Furthermore, every 12execution of a smart contract happens in public and, in addition to that, 13the source code is often available. 14 15Of course you always have to consider how much is at stake: 16You can compare a smart contract with a web service that is open to the 17public (and thus, also to malicious actors) and perhaps even open source. 18If you only store your grocery list on that web service, you might not have 19to take too much care, but if you manage your bank account using that web service, 20you should be more careful. 21 22This section will list some pitfalls and general security recommendations but 23can, of course, never be complete. Also, keep in mind that even if your smart 24contract code is bug-free, the compiler or the platform itself might have a 25bug. A list of some publicly known security-relevant bugs of the compiler can 26be found in the :ref:`list of known bugs<known_bugs>`, which is also 27machine-readable. Note that there is a bug bounty program that covers the code 28generator of the Solidity compiler. 29 30As always, with open source documentation, please help us extend this section 31(especially, some examples would not hurt)! 32 33NOTE: In addition to the list below, you can find more security recommendations and best practices 34`in Guy Lando's knowledge list <https://github.com/guylando/KnowledgeLists/blob/master/EthereumSmartContracts.md>`_ and 35`the Consensys GitHub repo <https://consensys.github.io/smart-contract-best-practices/>`_. 36 37******** 38Pitfalls 39******** 40 41Private Information and Randomness 42================================== 43 44Everything you use in a smart contract is publicly visible, even 45local variables and state variables marked ``private``. 46 47Using random numbers in smart contracts is quite tricky if you do not want 48miners to be able to cheat. 49 50Re-Entrancy 51=========== 52 53Any interaction from a contract (A) with another contract (B) and any transfer 54of Ether hands over control to that contract (B). This makes it possible for B 55to call back into A before this interaction is completed. To give an example, 56the following code contains a bug (it is just a snippet and not a 57complete contract): 58 59.. code-block:: solidity 60 61 // SPDX-License-Identifier: GPL-3.0 62 pragma solidity >=0.6.0 <0.9.0; 63 64 // THIS CONTRACT CONTAINS A BUG - DO NOT USE 65 contract Fund { 66 /// @dev Mapping of ether shares of the contract. 67 mapping(address => uint) shares; 68 /// Withdraw your share. 69 function withdraw() public { 70 if (payable(msg.sender).send(shares[msg.sender])) 71 shares[msg.sender] = 0; 72 } 73 } 74 75The problem is not too serious here because of the limited gas as part 76of ``send``, but it still exposes a weakness: Ether transfer can always 77include code execution, so the recipient could be a contract that calls 78back into ``withdraw``. This would let it get multiple refunds and 79basically retrieve all the Ether in the contract. In particular, the 80following contract will allow an attacker to refund multiple times 81as it uses ``call`` which forwards all remaining gas by default: 82 83.. code-block:: solidity 84 85 // SPDX-License-Identifier: GPL-3.0 86 pragma solidity >=0.6.2 <0.9.0; 87 88 // THIS CONTRACT CONTAINS A BUG - DO NOT USE 89 contract Fund { 90 /// @dev Mapping of ether shares of the contract. 91 mapping(address => uint) shares; 92 /// Withdraw your share. 93 function withdraw() public { 94 (bool success,) = msg.sender.call{value: shares[msg.sender]}(""); 95 if (success) 96 shares[msg.sender] = 0; 97 } 98 } 99 100To avoid re-entrancy, you can use the Checks-Effects-Interactions pattern as 101outlined further below: 102 103.. code-block:: solidity 104 105 // SPDX-License-Identifier: GPL-3.0 106 pragma solidity >=0.6.0 <0.9.0; 107 108 contract Fund { 109 /// @dev Mapping of ether shares of the contract. 110 mapping(address => uint) shares; 111 /// Withdraw your share. 112 function withdraw() public { 113 uint share = shares[msg.sender]; 114 shares[msg.sender] = 0; 115 payable(msg.sender).transfer(share); 116 } 117 } 118 119Note that re-entrancy is not only an effect of Ether transfer but of any 120function call on another contract. Furthermore, you also have to take 121multi-contract situations into account. A called contract could modify the 122state of another contract you depend on. 123 124Gas Limit and Loops 125=================== 126 127Loops that do not have a fixed number of iterations, for example, loops that depend on storage values, have to be used carefully: 128Due to the block gas limit, transactions can only consume a certain amount of gas. Either explicitly or just due to 129normal operation, the number of iterations in a loop can grow beyond the block gas limit which can cause the complete 130contract to be stalled at a certain point. This may not apply to ``view`` functions that are only executed 131to read data from the blockchain. Still, such functions may be called by other contracts as part of on-chain operations 132and stall those. Please be explicit about such cases in the documentation of your contracts. 133 134Sending and Receiving Ether 135=========================== 136 137- Neither contracts nor "external accounts" are currently able to prevent that someone sends them Ether. 138 Contracts can react on and reject a regular transfer, but there are ways 139 to move Ether without creating a message call. One way is to simply "mine to" 140 the contract address and the second way is using ``selfdestruct(x)``. 141 142- If a contract receives Ether (without a function being called), 143 either the :ref:`receive Ether <receive-ether-function>` 144 or the :ref:`fallback <fallback-function>` function is executed. 145 If it does not have a receive nor a fallback function, the Ether will be 146 rejected (by throwing an exception). During the execution of one of these 147 functions, the contract can only rely on the "gas stipend" it is passed (2300 148 gas) being available to it at that time. This stipend is not enough to modify 149 storage (do not take this for granted though, the stipend might change with 150 future hard forks). To be sure that your contract can receive Ether in that 151 way, check the gas requirements of the receive and fallback functions 152 (for example in the "details" section in Remix). 153 154- There is a way to forward more gas to the receiving contract using 155 ``addr.call{value: x}("")``. This is essentially the same as ``addr.transfer(x)``, 156 only that it forwards all remaining gas and opens up the ability for the 157 recipient to perform more expensive actions (and it returns a failure code 158 instead of automatically propagating the error). This might include calling back 159 into the sending contract or other state changes you might not have thought of. 160 So it allows for great flexibility for honest users but also for malicious actors. 161 162- Use the most precise units to represent the wei amount as possible, as you lose 163 any that is rounded due to a lack of precision. 164 165- If you want to send Ether using ``address.transfer``, there are certain details to be aware of: 166 167 1. If the recipient is a contract, it causes its receive or fallback function 168 to be executed which can, in turn, call back the sending contract. 169 2. Sending Ether can fail due to the call depth going above 1024. Since the 170 caller is in total control of the call depth, they can force the 171 transfer to fail; take this possibility into account or use ``send`` and 172 make sure to always check its return value. Better yet, write your 173 contract using a pattern where the recipient can withdraw Ether instead. 174 3. Sending Ether can also fail because the execution of the recipient 175 contract requires more than the allotted amount of gas (explicitly by 176 using :ref:`require <assert-and-require>`, :ref:`assert <assert-and-require>`, 177 :ref:`revert <assert-and-require>` or because the 178 operation is too expensive) - it "runs out of gas" (OOG). If you 179 use ``transfer`` or ``send`` with a return value check, this might 180 provide a means for the recipient to block progress in the sending 181 contract. Again, the best practice here is to use a :ref:`"withdraw" 182 pattern instead of a "send" pattern <withdrawal_pattern>`. 183 184Call Stack Depth 185================ 186 187External function calls can fail any time because they exceed the maximum 188call stack size limit of 1024. In such situations, Solidity throws an exception. 189Malicious actors might be able to force the call stack to a high value 190before they interact with your contract. Note that, since `Tangerine Whistle <https://eips.ethereum.org/EIPS/eip-608>`_ hardfork, the `63/64 rule <https://eips.ethereum.org/EIPS/eip-150>`_ makes call stack depth attack impractical. Also note that the call stack and the expression stack are unrelated, even though both have a size limit of 1024 stack slots. 191 192Note that ``.send()`` does **not** throw an exception if the call stack is 193depleted but rather returns ``false`` in that case. The low-level functions 194``.call()``, ``.delegatecall()`` and ``.staticcall()`` behave in the same way. 195 196Authorized Proxies 197================== 198 199If your contract can act as a proxy, i.e. if it can call arbitrary contracts 200with user-supplied data, then the user can essentially assume the identity 201of the proxy contract. Even if you have other protective measures in place, 202it is best to build your contract system such that the proxy does not have 203any permissions (not even for itself). If needed, you can accomplish that 204using a second proxy: 205 206.. code-block:: solidity 207 208 // SPDX-License-Identifier: GPL-3.0 209 pragma solidity ^0.8.0; 210 contract ProxyWithMoreFunctionality { 211 PermissionlessProxy proxy; 212 213 function callOther(address _addr, bytes memory _payload) public 214 returns (bool, bytes memory) { 215 return proxy.callOther(_addr, _payload); 216 } 217 // Other functions and other functionality 218 } 219 220 // This is the full contract, it has no other functionality and 221 // requires no privileges to work. 222 contract PermissionlessProxy { 223 function callOther(address _addr, bytes memory _payload) public 224 returns (bool, bytes memory) { 225 return _addr.call(_payload); 226 } 227 } 228 229tx.origin 230========= 231 232Never use tx.origin for authorization. Let's say you have a wallet contract like this: 233 234.. code-block:: solidity 235 236 // SPDX-License-Identifier: GPL-3.0 237 pragma solidity >=0.7.0 <0.9.0; 238 // THIS CONTRACT CONTAINS A BUG - DO NOT USE 239 contract TxUserWallet { 240 address owner; 241 242 constructor() { 243 owner = msg.sender; 244 } 245 246 function transferTo(address payable dest, uint amount) public { 247 // THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin 248 require(tx.origin == owner); 249 dest.transfer(amount); 250 } 251 } 252 253Now someone tricks you into sending Ether to the address of this attack wallet: 254 255.. code-block:: solidity 256 257 // SPDX-License-Identifier: GPL-3.0 258 pragma solidity >=0.7.0 <0.9.0; 259 interface TxUserWallet { 260 function transferTo(address payable dest, uint amount) external; 261 } 262 263 contract TxAttackWallet { 264 address payable owner; 265 266 constructor() { 267 owner = payable(msg.sender); 268 } 269 270 receive() external payable { 271 TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance); 272 } 273 } 274 275If your wallet had checked ``msg.sender`` for authorization, it would get the address of the attack wallet, instead of the owner address. But by checking ``tx.origin``, it gets the original address that kicked off the transaction, which is still the owner address. The attack wallet instantly drains all your funds. 276 277.. _underflow-overflow: 278 279Two's Complement / Underflows / Overflows 280========================================= 281 282As in many programming languages, Solidity's integer types are not actually integers. 283They resemble integers when the values are small, but cannot represent arbitrarily large numbers. 284 285The following code causes an overflow because the result of the addition is too large 286to be stored in the type ``uint8``: 287 288.. code-block:: solidity 289 290 uint8 x = 255; 291 uint8 y = 1; 292 return x + y; 293 294Solidity has two modes in which it deals with these overflows: Checked and Unchecked or "wrapping" mode. 295 296The default checked mode will detect overflows and cause a failing assertion. You can disable this check 297using ``unchecked { ... }``, causing the overflow to be silently ignored. The above code would return 298``0`` if wrapped in ``unchecked { ... }``. 299 300Even in checked mode, do not assume you are protected from overflow bugs. 301In this mode, overflows will always revert. If it is not possible to avoid the 302overflow, this can lead to a smart contract being stuck in a certain state. 303 304In general, read about the limits of two's complement representation, which even has some 305more special edge cases for signed numbers. 306 307Try to use ``require`` to limit the size of inputs to a reasonable range and use the 308:ref:`SMT checker<smt_checker>` to find potential overflows. 309 310.. _clearing-mappings: 311 312Clearing Mappings 313================= 314 315The Solidity type ``mapping`` (see :ref:`mapping-types`) is a storage-only 316key-value data structure that does not keep track of the keys that were 317assigned a non-zero value. Because of that, cleaning a mapping without extra 318information about the written keys is not possible. 319If a ``mapping`` is used as the base type of a dynamic storage array, deleting 320or popping the array will have no effect over the ``mapping`` elements. The 321same happens, for example, if a ``mapping`` is used as the type of a member 322field of a ``struct`` that is the base type of a dynamic storage array. The 323``mapping`` is also ignored in assignments of structs or arrays containing a 324``mapping``. 325 326.. code-block:: solidity 327 328 // SPDX-License-Identifier: GPL-3.0 329 pragma solidity >=0.6.0 <0.9.0; 330 331 contract Map { 332 mapping (uint => uint)[] array; 333 334 function allocate(uint _newMaps) public { 335 for (uint i = 0; i < _newMaps; i++) 336 array.push(); 337 } 338 339 function writeMap(uint _map, uint _key, uint _value) public { 340 array[_map][_key] = _value; 341 } 342 343 function readMap(uint _map, uint _key) public view returns (uint) { 344 return array[_map][_key]; 345 } 346 347 function eraseMaps() public { 348 delete array; 349 } 350 } 351 352Consider the example above and the following sequence of calls: ``allocate(10)``, 353``writeMap(4, 128, 256)``. 354At this point, calling ``readMap(4, 128)`` returns 256. 355If we call ``eraseMaps``, the length of state variable ``array`` is zeroed, but 356since its ``mapping`` elements cannot be zeroed, their information stays alive 357in the contract's storage. 358After deleting ``array``, calling ``allocate(5)`` allows us to access 359``array[4]`` again, and calling ``readMap(4, 128)`` returns 256 even without 360another call to ``writeMap``. 361 362If your ``mapping`` information must be deleted, consider using a library similar to 363`iterable mapping <https://github.com/ethereum/dapp-bin/blob/master/library/iterable_mapping.sol>`_, 364allowing you to traverse the keys and delete their values in the appropriate ``mapping``. 365 366Minor Details 367============= 368 369- Types that do not occupy the full 32 bytes might contain "dirty higher order bits". 370 This is especially important if you access ``msg.data`` - it poses a malleability risk: 371 You can craft transactions that call a function ``f(uint8 x)`` with a raw byte argument 372 of ``0xff000001`` and with ``0x00000001``. Both are fed to the contract and both will 373 look like the number ``1`` as far as ``x`` is concerned, but ``msg.data`` will 374 be different, so if you use ``keccak256(msg.data)`` for anything, you will get different results. 375 376*************** 377Recommendations 378*************** 379 380Take Warnings Seriously 381======================= 382 383If the compiler warns you about something, you should change it. 384Even if you do not think that this particular warning has security 385implications, there might be another issue buried beneath it. 386Any compiler warning we issue can be silenced by slight changes to the 387code. 388 389Always use the latest version of the compiler to be notified about all recently 390introduced warnings. 391 392Messages of type ``info`` issued by the compiler are not dangerous, and simply 393represent extra suggestions and optional information that the compiler thinks 394might be useful to the user. 395 396Restrict the Amount of Ether 397============================ 398 399Restrict the amount of Ether (or other tokens) that can be stored in a smart 400contract. If your source code, the compiler or the platform has a bug, these 401funds may be lost. If you want to limit your loss, limit the amount of Ether. 402 403Keep it Small and Modular 404========================= 405 406Keep your contracts small and easily understandable. Single out unrelated 407functionality in other contracts or into libraries. General recommendations 408about source code quality of course apply: Limit the amount of local variables, 409the length of functions and so on. Document your functions so that others 410can see what your intention was and whether it is different than what the code does. 411 412Use the Checks-Effects-Interactions Pattern 413=========================================== 414 415Most functions will first perform some checks (who called the function, 416are the arguments in range, did they send enough Ether, does the person 417have tokens, etc.). These checks should be done first. 418 419As the second step, if all checks passed, effects to the state variables 420of the current contract should be made. Interaction with other contracts 421should be the very last step in any function. 422 423Early contracts delayed some effects and waited for external function 424calls to return in a non-error state. This is often a serious mistake 425because of the re-entrancy problem explained above. 426 427Note that, also, calls to known contracts might in turn cause calls to 428unknown contracts, so it is probably better to just always apply this pattern. 429 430Include a Fail-Safe Mode 431======================== 432 433While making your system fully decentralised will remove any intermediary, 434it might be a good idea, especially for new code, to include some kind 435of fail-safe mechanism: 436 437You can add a function in your smart contract that performs some 438self-checks like "Has any Ether leaked?", 439"Is the sum of the tokens equal to the balance of the contract?" or similar things. 440Keep in mind that you cannot use too much gas for that, so help through off-chain 441computations might be needed there. 442 443If the self-check fails, the contract automatically switches into some kind 444of "failsafe" mode, which, for example, disables most of the features, hands over 445control to a fixed and trusted third party or just converts the contract into 446a simple "give me back my money" contract. 447 448Ask for Peer Review 449=================== 450 451The more people examine a piece of code, the more issues are found. 452Asking people to review your code also helps as a cross-check to find out whether your code 453is easy to understand - a very important criterion for good smart contracts. 454