静态分析是相对容易掌握的工具,对开发复杂的Defi应用非常有帮助。
合约的安全性自动化检测有静态分析、动态分析和形式化验证。静态分析不执行合约代码,通过对合约代码做模式匹配或者语义分析来检测漏洞。动态分析需要执行合约,通过大量的模糊测试来观察合约的状态是否会出现问题。形式化验证是将合约的业务逻辑用数学表达式来描述,只要证明数学表达式是正确的,则合约的业务逻辑也是正确的(不代表合约的实现没有问题)。
静态分析的优点是使用简单,速度快,但只能检测已知的[安全漏洞](https://learnblockchain.cn/article/3625)。动态分析能检测出未知的安全问题,但是成本高、速度慢。形式化验证的使用范围窄,比较适用于一些公共库合约。
开发者对合约做静态分析是最基本的要求,使用静态分析工具可以快速检测是否存在一些常见的漏洞,比如:
* 权限缺失,比如Oracle的更新没有设置权限
* 重入,这个出的问题最多
* 整数溢出
* DDOS,攻击或者缺陷会导致合约无法执行正常的业务逻辑
* 价格操纵
但静态分析工具不能检测出跟业务逻辑特定相关的问题,还需要开发人员通过自检去做人工静态分析。
## Solhint
[Solhint](https://github.com/protofire/solhint)能提供一些代码规范和安全检查,一些推荐的代码规范比如:
* 对字符串使用双引号
* 使用驼峰命名规则
* 明确指定状态变量的可见性
* 避免使用`call`,`delegatecall`等底层操作码
* 避免使用`tx.origin`
* 同一个方法中避免多次使用`msg.value`
* 避免使用`block.number`和`block.timestamp`
Solhint的能力较弱,只能做到语法层面的一些检查,但对规范代码比较有用。
## Semgrep
[Semgrep](https://semgrep.dev/)是一个通用型的静态分析工具,支持多种语言,对solidity的支持目前还较弱。Semgrep跟Solhint一样也是采用模式匹配来进行检测,Solhint的规则是内置的,Semgrep能自定义规则。比如下面这个规则[compound-sweeptoken-not-restricted](https://github.com/Raz0r/semgrep-smart-contracts/blob/master/solidity/compound-sweeptoken-not-restricted.yaml)
“`yaml
rules:
–
id: compound-sweeptoken-not-restricted
message: function sweepToken is allowed to be called by anyone
metadata:
references:
– https://medium.com/chainsecurity/trueusd-compound-vulnerability-bc5b696d29e2
– https://chainsecurity.com/security-audit/compound-ctoken/
– https://blog.openzeppelin.com/compound-comprehensive-protocol-audit/
– https://etherscan.io/address/0xa035b9e130f2b1aedc733eefb1c67ba4c503491f # Compound
category: access-control
tags:
– compound
– tusd
patterns:
– pattern-inside: |
function sweepToken(…) {
…
}
– pattern: token.transfer(…);
– pattern-not-inside: |
require(msg.sender == admin, “…”);
…
languages:
– solidity
severity: WARNING
“`
这个规则专门针对的Compound曾经出现过的TUSD漏洞,由于有很多其它链的项目fork了Compound,因此这个规则可以快速检测出这些项目是否有类似的问题。
## Slither
[Slither](https://github.com/crytic/slither)的功能包括:
* 漏洞自动检测
* 提供代码优化建议
* 展现代码的拓扑结构
* 通过API能自定义漏洞检测规则
Slither的原理是将Solidty抽象语法树(AST)作为输入:
* 第一步,先解析出合约间的继承图、控制循环图(CFG)和表达式。
* 第二步,将合约代码转换成SlitherIR(一种内部表达码)。
* 第三步,对SlitherIR执行一系列单一静态分析(SSA)来完成漏洞检测。

Solhint和Semgrep都是在语法级别进行规则匹配,相比而言Slither能在语义级别进行分析。Slither也可以通过[插件](https://github.com/crytic/slither/tree/ae7c410938b616d993e6c27678f6e48d9a4d7dd6/plugin_example)来实现自定义的漏洞检测规则,实现上要比Semgrep这种配置文件的方式复杂点。
## 自检
相对于自动检测工具而言,开发者的自检能完成更复杂的静态分析。比如
“`solidity
address[] public minters;
function setMinter() external {
minters.push(msg.sender);
}
“`
静态分析工具没法知道修改`minters`这个状态变量需要什么权限,因为这属于业务逻辑的范围。再比如
“`solidity
if (a > 100) {
b++;
}
“`
如果开发者误将`a >= 100`写成了`a > 100`,这种业务逻辑错误静态分析工具也没法处理。
合约的业务逻辑都是主要在接口中实现的,因此接口检查就很重要:
* 参数是否有校验,尤其是需要注意是否有任意输入。
* 接口必须是external或者public吗?如果把一个internal或者private接口暴露出去会非常危险。
* 需要加payable吗?不加的话没法接收eth,但是若无必要则一定不要加。
* 接口会修改状态变量吗?修改这些变量需要权限吗?这一点往往是静态分析工具无法检测到的漏洞。
* 通过call或者delegatecall的调用对象是可信的吗?
* 外部调用需要设置gasLimit吗?外表调用的返回结果需要处理吗?
* 在有外部调用的代码前后遵守了Checks-Effects-Interactions规范吗?
* 外部调用能重入到合约中的其它接口然后通过旁路回到本接口吗?
如果接口涉及到Token的转移,则需要的检查有:
* 如果Token转移过程中内部扣费会影响业务逻辑吗?
* Token转移过程中会有钩子函数回调发送者或者接收者吗?
* Token如果是可升级合约,对业务逻辑有影响吗?
* 有使用地址的eth余额参与控制逻辑吗?eth的余额是可以通过挖矿或者selfdestruct强制增加的。
对于借贷相关的合约,一般需要使用价格,则需要的检查有:
* Offchain oracle是可靠的吗?
* Onchain oracle的价格容易被操纵吗?
* LP token的价格计算算法是正确的吗?
总之,所有的检查都围绕几个核心:
* 敏感的权限是否能被转移到任意地址
* 资产是否有可能被较小的代价转走
* 资产是否有可能无法取出
## 结束语
静态分析是相对容易掌握的工具,对开发复杂的Defi应用非常有帮助。不同的静态分析工具可以结合使用,可以先使用Solhint来规范代码,然后使用Semgrep来识别已知的漏洞,接着使用Slither来识别一些语义级别的问题。
开发者更需要自己检查代码,最好是邀请同行互审。最后还是需要审计机构审计代码,不过也不要迷信审计机构,尤其是当Defi的业务逻辑比较复杂的时候,审计机构不一定能精确地理解每一个业务逻辑。
简介
合约的安全性自动化检测有静态分析、动态分析和形式化验证。静态分析不执行合约代码,通过对合约代码做模式匹配或者语义分析来检测漏洞。动态分析需要执行合约,通过大量的模糊测试来观察合约的状态是否会出现问题。形式化验证是将合约的业务逻辑用数学表达式来描述,只要证明数学表达式是正确的,则合约的业务逻辑也是正确的(不代表合约的实现没有问题)。
静态分析的优点是使用简单,速度快,但只能检测已知的安全漏洞。动态分析能检测出未知的安全问题,但是成本高、速度慢。形式化验证的使用范围窄,比较适用于一些公共库合约。
开发者对合约做静态分析是最基本的要求,使用静态分析工具可以快速检测是否存在一些常见的漏洞,比如:
- 权限缺失,比如Oracle的更新没有设置权限
- 重入,这个出的问题最多
- 整数溢出
- DDOS,攻击或者缺陷会导致合约无法执行正常的业务逻辑
- 价格操纵
但静态分析工具不能检测出跟业务逻辑特定相关的问题,还需要开发人员通过自检去做人工静态分析。
Solhint
Solhint能提供一些代码规范和安全检查,一些推荐的代码规范比如:
- 对字符串使用双引号
- 使用驼峰命名规则
- 明确指定状态变量的可见性
- 避免使用
call
,delegatecall
等底层操作码 - 避免使用
tx.origin
- 同一个方法中避免多次使用
msg.value
- 避免使用
block.number
和block.timestamp
Solhint的能力较弱,只能做到语法层面的一些检查,但对规范代码比较有用。
Semgrep
Semgrep是一个通用型的静态分析工具,支持多种语言,对solidity的支持目前还较弱。Semgrep跟Solhint一样也是采用模式匹配来进行检测,Solhint的规则是内置的,Semgrep能自定义规则。比如下面这个规则compound-sweeptoken-not-restricted
rules:
-
id: compound-sweeptoken-not-restricted
message: function sweepToken is allowed to be called by anyone
metadata:
references:
- https://medium.com/chainsecurity/trueusd-compound-vulnerability-bc5b696d29e2
- https://chainsecurity.com/security-audit/compound-ctoken/
- https://blog.openzeppelin.com/compound-comprehensive-protocol-audit/
- https://etherscan.io/address/0xa035b9e130f2b1aedc733eefb1c67ba4c503491f # Compound
category: access-control
tags:
- compound
- tusd
patterns:
- pattern-inside: |
function sweepToken(...) {
...
}
- pattern: token.transfer(...);
- pattern-not-inside: |
require(msg.sender == admin, "...");
...
languages:
- solidity
severity: WARNING
这个规则专门针对的Compound曾经出现过的TUSD漏洞,由于有很多其它链的项目fork了Compound,因此这个规则可以快速检测出这些项目是否有类似的问题。
Slither
Slither的功能包括:
- 漏洞自动检测
- 提供代码优化建议
- 展现代码的拓扑结构
- 通过API能自定义漏洞检测规则
Slither的原理是将Solidty抽象语法树(AST)作为输入:
- 第一步,先解析出合约间的继承图、控制循环图(CFG)和表达式。
- 第二步,将合约代码转换成SlitherIR(一种内部表达码)。
- 第三步,对SlitherIR执行一系列单一静态分析(SSA)来完成漏洞检测。
Solhint和Semgrep都是在语法级别进行规则匹配,相比而言Slither能在语义级别进行分析。Slither也可以通过插件来实现自定义的漏洞检测规则,实现上要比Semgrep这种配置文件的方式复杂点。
自检
相对于自动检测工具而言,开发者的自检能完成更复杂的静态分析。比如
address[] public minters;
function setMinter() external {
minters.push(msg.sender);
}
静态分析工具没法知道修改minters
这个状态变量需要什么权限,因为这属于业务逻辑的范围。再比如
if (a > 100) {
b++;
}
如果开发者误将a >= 100
写成了a > 100
,这种业务逻辑错误静态分析工具也没法处理。
合约的业务逻辑都是主要在接口中实现的,因此接口检查就很重要:
- 参数是否有校验,尤其是需要注意是否有任意输入。
- 接口必须是external或者public吗?如果把一个internal或者private接口暴露出去会非常危险。
- 需要加payable吗?不加的话没法接收eth,但是若无必要则一定不要加。
- 接口会修改状态变量吗?修改这些变量需要权限吗?这一点往往是静态分析工具无法检测到的漏洞。
- 通过call或者delegatecall的调用对象是可信的吗?
- 外部调用需要设置gasLimit吗?外表调用的返回结果需要处理吗?
- 在有外部调用的代码前后遵守了Checks-Effects-Interactions规范吗?
- 外部调用能重入到合约中的其它接口然后通过旁路回到本接口吗?
如果接口涉及到Token的转移,则需要的检查有:
- 如果Token转移过程中内部扣费会影响业务逻辑吗?
- Token转移过程中会有钩子函数回调发送者或者接收者吗?
- Token如果是可升级合约,对业务逻辑有影响吗?
- 有使用地址的eth余额参与控制逻辑吗?eth的余额是可以通过挖矿或者selfdestruct强制增加的。
对于借贷相关的合约,一般需要使用价格,则需要的检查有:
- Offchain oracle是可靠的吗?
- Onchain oracle的价格容易被操纵吗?
- LP token的价格计算算法是正确的吗?
总之,所有的检查都围绕几个核心:
- 敏感的权限是否能被转移到任意地址
- 资产是否有可能被较小的代价转走
- 资产是否有可能无法取出
结束语
静态分析是相对容易掌握的工具,对开发复杂的Defi应用非常有帮助。不同的静态分析工具可以结合使用,可以先使用Solhint来规范代码,然后使用Semgrep来识别已知的漏洞,接着使用Slither来识别一些语义级别的问题。
开发者更需要自己检查代码,最好是邀请同行互审。最后还是需要审计机构审计代码,不过也不要迷信审计机构,尤其是当Defi的业务逻辑比较复杂的时候,审计机构不一定能精确地理解每一个业务逻辑。
本文参与区块链开发网写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。
- 发表于 2022-06-23 12:02
- 阅读 ( 762 )
- 学分 ( 46 )
- 分类:Solidity