智能合约语法层面漏洞详解
Unused variables are allowed to be used in solidity, which will not cause direct safety problems, but unused variables will lead to increased calculation and unnecessary gas consumption, poor code quality and reduced code readability, so it is necessary to avoid the occurrence of unused variables as much as possible. Solidity中允许使用未使用的变量,它们不会带来直接的安全问题,但是未使用的变量会导致计算量增加以及不必要的气体消耗,使代码质量不佳,降低代码的可读性,因此需要尽可能避免未使用变量的出现。
Delete unused variables when writing smart contracts. 编写智能合约代码时,删除未使用的变量。
contract UnusedVariables {
function neverAccessed(int test) public pure returns (int) {
int z = 10;
if (test > z) {
// x is not used,please remove it
int x = test - z;
return test - z;
}
return z;
}
}
https://github.com/protofire/solhint/blob/master/docs/rules/best-practises/no-unused-vars.md
A smart contract can have and can only have a function without a name, and it can not have parameters or return values. This function is called a fallback function. When the contract is called but there is no other method matching or no data provided, the fallback function will be executed. At the same time, the payable keyword allows the function to receive the ether currency when it is called. If the fallback function does not Key word mark, the contract can not receive ether currency through normal transaction. 一个智能合约可以有且只能有一个没名字的函数同时也不能有参数 也不能有返回的值,该函数被称为回退函数,当合约被调用但没有其它方法匹配或没有数据提供时,就会执行回退函数,同时payable关键字允许函数被调用的时候接收以太币,如果回退函数没有被该关键字标记,则合约不能通过正常交易接收以太币。
If there is a fallback function in a smart contract, it must be marked with the payable keyword. 如果一个智能合约中存在回退函数,则必须使用payable关键字标记该函数。
contract FallbackPayable {
uint x;
function() public { x = 1; }
}
(4)Reference
https://github.com/protofire/solhint/blob/master/docs/rules/best-practises/payable-fallback.md
Solidity provides three functions: call, delegatecall, and callcode to realize the mutual call and interaction between contracts. call, callcode, and delegatecall have a great deal of freedom. call and callcode calls will change the value of msg, modify msg.sender as the address of the caller's contract, and delegatecall and callcode will copy the target code to their own environment for execution. Because of these flexible calls, these functions are abused or even unscrupulous by the contract developers Provide arbitrary call function, leading to various security vulnerabilities and risks. Solidity 中提供了 call、delegatecall、callcode 三个函数来实现合约之间相互调用及交互。 call、callcode、delegatecall调用的自由度极大,.call 与 callcode 调用会改变 msg 的值,会修改 msg.sender 为调用者合约的地址,delegatecall 与 callcode 会拷贝目标代码到自己的环境中执行.正是因为这些灵活调用,也导致了这些函数被合约开发者滥用,甚至肆无忌惮提供任意调用功能,导致了各种安全漏洞及风险.
It is necessary to use these underlying functions carefully,at the same time, we need to make strict restrictions on the calling contract address and callable functions.
The address of the contract itself cannot be easily used as a trusted address in the contract.
The calling function should be strictly restricted to avoid the hidden danger of calling any function.
1.需要谨慎的使用这些底层的函数,同时在使用时,需要对调用的合约地址、可调用的函数做严格的限制。
2.在合约中不能轻易将合约本身的地址作为可信地址。
3.调用的函数应该做严格的限制,避开调用任意函数的隐患。
contract CallTest {
address owner;
constructor() public {
owner = msg.sender;
}
function forward(address callee, bytes _data) public {
require(callee.call(_data));
}
}
https://github.com/protofire/solhint/blob/master/docs/rules/security/avoid-low-level-calls.md
Several functions in Solidity are deprecated,such as sha3、throw and suicide.Using them leads to reduced code quality. With new versions of the Solidity compiler, deprecated functions may result in side effects and compile errors. 有些函数在Solidity中被弃用,例如sha3、throw和suicide,使用它们会导致代码质量降低,在Solidity编译器的新的版本中,不推荐使用的函数可能会无法通过编译。
It is recommended to replace keccak256 with sha3 、replace selfdestruct with suicide 、replace revert with throw. 在编写智能合约时,建议使用keccak256函数代替sha3函数,使用 selfdestruct函数代替 suicide函数,使用 revert函数代替 throw函数。
contract DeprecatedSimple{
// Do everything that's deprecated, then commit suicide.
function useDeprecated() public constant {
bytes32 blockhash = block.blockhash(0);
bytes32 hashofhash = sha3(blockhash);
uint gas = msg.gas;
if (gas == 0) {
throw;
}
address(this).callcode();
var a = [1,2,3];
var (x, y, z) = (false, "test", 0);
suicide(address(0));
}
function () public {}
}
https://swcregistry.io/docs/SWC-111
Functions that do not have a function visibility type specified are public by default. This can lead to a vulnerability if a developer forgot to set the visibility and a malicious user is able to make unauthorized or unintended state changes. 没有指定可见性类型的函数默认可见性是public,如果开发人员忘记设置可见性,并且恶意用户能够进行未经授权或意外的状态更改,则可能会导致漏洞。
Functions can be specified as being external, public, internal or private. It is necessary to consider which visibility type is appropriate for a function. 函数的可见性类型有4种:external, public, internal or private,编写合约时,需要为函数添加正确的可见性类型。
contract HashForEther {
function withdrawWinnings() {
// Winner if the last 8 hex characters of the address are 0.
require(uint32(msg.sender) == 0);
_sendWinnings();
}
function _sendWinnings() {
msg.sender.transfer(this.balance);
}
}
https://github.com/protofire/solhint/blob/master/docs/rules/security/func-visibility.md https://swcregistry.io/docs/SWC-100
Explicitly marking the visibility of variables can make it easier for users to clarify their access to variables and avoid unauthorized or unexpected state changes by malicious users. 显式地标记变量的可见性可以更容易地明确使用者对变量的访问权,避恶意用户进行未经授权或意外的状态更改。
Variables in the smart contract can be specified as public, internal or private, and the visibility of all state variables needs to be explicitly defined. 智能合约中的变量可以指定为public、internal或private,需要显式定义所有状态变量的可见性。
contract TestStorage {
uint[] uintarray;
function testStorage() public {
uintarray.push(8000);
uintarray.push(9000);
}
}
https://github.com/protofire/solhint/blob/master/docs/rules/security/state-visibility.md https://swcregistry.io/docs/SWC-108
Solidity defines an assembly language, which can be embedded in the source code of solidity and used in the way of inline assembly. The inline assembly provided by solidity is a language close to the EVM alternative, allowing for use with solidity. Because EVM is stackable, sometimes it is difficult to locate the stack. Solidty's inline assembly provides alternative features to solve various problems caused by handwritten underlying code. However, inline assembly language does not have many security mechanisms provided by solidity, and it is difficult to write inline compilation language. Solidity定义了一个汇编语言,这个汇编语言可以嵌入到Solidity源码中,以内联汇编的方式使用。Solidity提供的内联汇编是一种接近于EVM替代的语言,允许与Solidity结合使用。由于EVM是栈式的,所以有时定位栈比较麻烦,Solidty的内联汇编提供了替代的特性,解决手写底层代码带来的各种问题。但内联汇编语言没有Solidity提供的多种安全机制,并且内联编译语言编写较难。
When writing smart contracts, try not to use inline assembly language. 编写智能合约时,尽量不要使用内联汇编语言。
contract InlineAssembly{
function test(){
assembly{
let freemem_pointer :=mload(0x40) //0x80
mstore(add(freemem_pointer,0x00),"36e5236fcd4c610449678014f0d085")
mstore(add(freemem_pointer,0x20),"36e5236fcd4c610449678014f0d086")
let arr1:=mload(freemem_pointer)
mstore(add(freemem_pointer,0x40),arr1)
}
}
}
https://github.com/protofire/solhint/blob/master/docs/rules/security/no-inline-assembly.md https://www.tryblockchain.org/blockchain-solidity-assembly.html
The send function always calls the fallback function. If the fallback function in a malicious account implements an infinite loop when sending a series of accounts, it will cause all send failures because the gas is exhausted. At the same time, operations such as writing data to the blockchain, creating a contract, calling an external function, sending ether will out-of-gas. send函数总是会调用fallback函数,如果对一系列帐户进行send操作时,其中某个恶意帐户中的fallback函数实现了一个无限循环,将会因为gas耗尽,导致所有send失败,同时,向区块链中写数据、创建一个合约、调用一个external的函数、发送ether等操作均会超过fallback函数限定的gas值。
When writing a smart contract, you can do some logging operations in the fallback function to avoid some complex operations. 编写智能合约时,在fallback函数中进行一些日志操作即可,避免执行一些复杂操作。
pragma solidity ^0.4.0;
contract FallbackFailOnGasLimit{
uint someStorage;
event fallbackTrigged(bytes);
function() payable{
fallbackTrigged(msg.data);
//将因为写入操作失败,注释掉下面这行,将会执行成功
someStorage = 1;
}
function callFallback() returns (bool){
return this.send(0);
}
}
https://github.com/protofire/solhint/blob/master/docs/rules/security/no-complex-fallback.md
When invoking external contracts, the naming of variables, methods, and contract interfaces should indicate that it may be unsafe to interact with them. 当调用外部合约时,变量、方法、合约接口的命名应该表明和他们交互可能是不安全的。
External contract calls should be marked trusted or untrusted. 外部合约调用时应该标记关键字trusted或untrusted。
Contract MarkContract{
function makeWithdrawal(uint amount) { // Isn't clear that this function is potentially unsafe
Bank.withdraw(amount);
}
}
https://github.com/protofire/solhint/blob/master/docs/rules/security/mark-callable-contracts.md
External calls can fail accidentally or deliberately. To minimize the damage caused by such failures, it is often better to isolate each external call into its own transaction that can be initiated by the recipient of the call. Avoid multiple ether transfers in a single transaction. 外部调用可能意外或故意失败,为了最大限度地减少此类故障造成的损害,通常最好将每个外部调用隔离到自己的事务中,该事务可以由调用的接收者发起,同时一个事务中不能出现多个发送以太币的函数。
Avoid multiple calls of "send" method in single transaction. 避免在单个事务中多次调用“send”方法。
pragma solidity ^0.4.24;
contract MutipleSend{
mapping (address => uint256) public balanceOf;
// INSECURE
function transfer(address a, address b,uint256 _value) {
/* Check if sender has balance */
require(balanceOf[msg.sender] >= _value);
/* Send ethereum */
a.send(_value);
b.send(_value);
/* Add and subtract new balances */
balanceOf[msg.sender] -= _value;
}
}
https://github.com/protofire/solhint/blob/master/docs/rules/security/multiple-sends.md
when using solidity to write smart contracts, we will use revert and require to assert. In order to get error information when assertion fails, we need to add error information to the judgment condition.
在使用solidity写智能合约的时候,会使用revert和require来进行断言,为了在断言失败得到出错信息时,需要在判断条件中添加出错信息。
Require or revert statement must have a reason string and check that each reason string is at most N characters long. Require或revert语句必须有一个出错信息字符串,并检查每个出错信息字符串的长度不超过N个字符。
contract ReasonString{
function b() public {
require(!has(role, account));
role.bearer[account] = true;
role.bearer[account] = true;
}
}
https://github.com/protofire/solhint/blob/master/docs/rules/best-practises/reason-string.md
Use double quotes for string literals,default quotes values is “double”.
对字符串文本使用双引号,默认引号值为“double”.
Use double quotes for string literals.
对字符串文本使用双引号。
contract A {
string private a = 'test';
}
https://github.com/protofire/solhint/blob/master/docs/rules/miscellaneous/quotes.md
The constructor in the smart contract is a special function. Some key initialization operations will be performed in the constructor. The constructor of the smart contract needs to be written first. 智能合约中的构造函数是一个比较特殊的函数,在构造函数里会执行一些关键的初始化操作,需要首先编写智能合约的构造函数。
Constructor must be first other functions in the smart contract. 构造函数的顺序优先合约中的其他函数。
contract A {
function () public payable {}
constructor() public {}
}
https://github.com/protofire/solhint/blob/master/docs/rules/order/func-order.md
The modifier is the function modifier, which can easily control the logic and make the contract structure clear and reasonable. When using the modifier, you need to indicate the visibility of the function first. modifier 为函数的修改器,可以很方便的控制逻辑,让合约结构清晰合理,使用修改器时需要先标明函数的可见性。
Visibility modifier must be first in list of modifiers.
contract A {
function a() ownable() public payable {}
}
https://github.com/protofire/solhint/blob/master/docs/rules/order/visibility-modifier-order.md
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!