BTC
ETH
HTX
SOL
BNB
View Market
简中
繁中
English
日本語
한국어
ภาษาไทย
Tiếng Việt

Smart contract development must-read: These 10 Solidity security issues cannot be ignored

登链社区
特邀专栏作者
This article is about 3645 words, reading the full article takes about 6 minutes
The security of smart contracts is worrying, learn about 10 common security issues in Solidity in 2020.
AI Summary
Expand
The security of smart contracts is worrying, learn about 10 common security issues in Solidity in 2020.

Editor's Note: This article comes fromDenglian Community, reprinted by Odaily with authorization.

Editor's Note: This article comes from

Denglian Community

Denglian Community

, reprinted by Odaily with authorization.

In 2018, we (CheckMarx) did a preliminary study on the state of smart contract security, focusing on smart contracts written in Solidity[1]. At the time, we compiled the top 10 smart contract security issues based on publicly available contract source code. Two years on, it’s time to update research and assess how smart contract security has progressed.

Other issues worthy of attention

While it's nice to have a security issue ranking, it tends to have interesting details because some of the details don't quite line up with the ranking list. Before digging deeper into the top 10 issues, it is necessary to explain some of the highlights of the original research that deserve attention:

In 2018, the top two issues were external contract denial of service and reentrancy. But now these problems have eased (but still cause for concern). You can learn more about Reentrancy from our research blog: A Security Perspective on Smart Contracts [2].

Translator's Note: In fact, due to the combination of DeFi applications (such as flash loans), there have been many serious re-entry attacks.

Now that Solidity v0.6.x is released [3] and it brings many breaking changes [4], yet 50% of the scanned smart contracts are not even ready for Solidity v0.5.0 compiler. Another 30% of smart contracts use outdated syntax (for example: using sha3, throw, constant, etc.), and 83% of contracts have specification issues (pragmas) in the specified compiler version.

Translator's Note: Solidity 0.6 is more semantically clear (for example, the 0.6 version is upgraded in terms of inheritance [5]), which helps the compiler to find problems in time and make the code safer.

Although visibility issues[6] did not appear in the top 10 in 2018, nor in the top 10 this year, the 48% increase in visibility issues is noteworthy.

The table below compares the changes between the 2018 and 2020 Top 10 FAQ lists. These issues are ordered by severity and prevalence:

1. Unchecked external calls

   if(!addr.send(1)) {
     revert()
   }

Unchecked external calls are the third most common problem on the 2018 Solidity Top 10 Security Issues list. With the first two now resolved, unchecked external calls are the most common issue on the 2020 update list.

Solidity's underlying call method, (such as address.call()) will not throw an exception. Instead, when an error is encountered, false is returned.

And if you use a contract to call ExternalContract.doSomething(), if doSomething() throws an exception, the exception will continue to "bubble" and propagate.

Unsuccessful cases should be handled explicitly by checking the return value, the following ether transfer using addr.send() is a good example, this is also valid for other external calls.

   for(uint256 i=0; i< elements.length; i++) {
       // do something
   }

2. High cost cycle

High Cost Loops moved from fourth to second on the Solidity Security list. The number of smart contracts affected by the issue has grown by almost 30%.

Everyone knows that calculations on Ethereum require payment. Therefore, reducing the computation required to complete an operation is not just an optimization problem (efficiency), but also a cost one.

Looping is an expensive operation, here's a good example: the more elements an array contains, the more iterations are required to complete the loop. Eventually, the infinite loop will use up all available gas.

If an attacker is able to influence the length of the element array, the above code will cause a denial of service (execution cannot break out of the loop). In the scanned smart contracts, 8% of the contracts were found to have array length manipulation issues.

3. Overpowered owners

   function calculateBonus(uint amount) returns (uint) {
       return amount/DELIMITER*BONUS;
   }

This is an emerging problem in Soldiity's top ten security issues, which affects about 16% of contracts, some contracts are closely related to their owners (Owner), and some functions can only be called by owner addresses, as shown in the following example:

Only the contract owner can call the doSomething() and doSomethingElse() functions: the former uses the onlyOwner decorator, while the latter implements it explicitly. This poses a serious risk: if the owner's private key is compromised, an attacker can take control of the contract.

4. Arithmetic precision problem

   function transferTo(address dest, uint amount) {    
         require(tx.origin == owner) {      
                  dest.transfer(amount);  
         }
   }

Solidity's data types are somewhat complicated due to the use of a 256-bit virtual machine (EVM[7]). Solidity does not provide floating-point arithmetic, and data types with less than 32 bytes will be packed into the same 32-byte slot. With this in mind, you should expect the following program precision issues:

As shown in the example above, a division performed before a multiplication can have huge rounding errors.

5. Depend on tx.origin

   for (uint i = border; i >= 0; i--) {  
         ans += i;
   }

Smart contracts should not rely on tx.origin for authentication, as a malicious contract could perform a man-in-the-middle attack and drain all funds. It is recommended to use msg.sender instead:<、>A detailed description of the Tx Origin attack can be found in Solidity's documentation [8]. Simply put, tx.origin is always the initial initiator account in the contract call chain, while msg.sender represents the direct caller. If the last contract in the chain relies on tx.origin for authentication, then calling the contract in the middle of the chain will be able to drain funds from the called contract, because the authentication does not check who (msg.sender) actually made the call.

6. Overflow (Overflow / Underflow)

Solidity's 256-bit virtual machine has overflow and underflow problems (Translator's Note: Because the result exceeds the value range, it is called overflow), here [9] has a specific analysis. Developers should take extra care when using the uint data type in a for loop condition, as it can lead to an infinite loop:

In the example above, when the value of i is 0, the next value is 2^256 -1, which makes the condition always true. Developers should try to use

, != and == for comparison.

   for (var i = 0; i < elements.length; i++) {
      // to something
   }

7. Unsafe type deduction

The issue moved up two places in Solidity's top 10 security issues list, and now affects more than 17% more smart contracts than before.

Solidity supports type inference, but it has some weird behaviors. For example, the literal 0 is inferred to be of type byte instead of the normally expected integer.

In the example below, the type of i is inferred to be uint8, because then being able to store the value of i as uint8 is sufficient. But if the elements array contains more than 256 elements, the following code overflows:

It is recommended to declare data types explicitly to avoid unexpected behavior and/or errors.

   if(!addr.send(1)) {    
      revert()
   }

Translator's Note: The var definition variable has been removed in Solidity 0.6 (there is no type deduction after Solidity 0.6), if you use the new compiler, it will not be a problem.

8. Incorrect Transfer

The issue dropped from sixth to eighth in Solidity's top 10 security issues list and currently affects less than 1% of smart contracts.

   for (uint i = 0; i < users.lenghth; i++) {
      users[i].transfer(amount);
   }

There are several ways to transfer ether between contracts. Although the official recommendation is to use the addr.transfer(x) function, we still found smart contracts that still use the send() function:

Note that addr.transfer(x) automatically raises an exception if the transfer is unsuccessful, again mitigating the problem of the first unchecked external call

9. In-cycle transfers

   if (timeHasCome == block.timestamp) {    
       winner.transfer(amount);
    }

When making ether transfers in the loop body, if one of the transfers fails (for example, a contract cannot receive), then the entire transaction will be rolled back.<、>In this example, an attacker could exploit this behavior to conduct a denial of service attack, preventing other users from receiving ether.

In 2018, the timestamp dependency problem ranked fifth, and it is important to remember that smart contracts run on multiple nodes at different times. The Ethereum Virtual Machine (EVM) does not provide clock time, and the now variable (alias for block.timestamp ) that is usually used to get a timestamp is actually an environment variable that miners can manipulate.

Summarize

Since the miner can manipulate the current environment variables, it can only be used in the inequality >,

= and <= use its value.

Summarize

Source link:securityboulevard.com

开发者
安全
Welcome to Join Odaily Official Community