EEA以太坊企业联盟安全规范第三版

Kopei article

​​当前版本(2025/3)​​:

https://entethalliance.org/specs/ethtrust-sl/v3/

​​本版本检查清单​​:

https://entethalliance.org/specs/ethtrust-sl/v3/checklist.html

​​最新编辑草案​​:

https://entethalliance.github.io/eta-registry/security-levels-spec.html

​​编辑​​:

EEA 编辑

​​最新发布版本URL​​:

https://entethalliance.org/specs/ethtrust-sl/

​​本版本贡献者​​:

除所有为之前版本做出贡献的人员外,以下人员对本版本规范有具体贡献:
Chaals Neville、Anish Agrawal (Olympix)、Kenan Bešić (ChainSecurity)、Daniel Burnett、Valerian Callens、Luke Ciattaglia (Hacken)、Christopher Cordi、
Ignacio Freire (Olympix)、Aminadav Glickshtein (EY)、Opal Graham (CertiK)、Channi Greenwall (Olympix)、James Harsh、Sebastian Holler、
George Kobakhidze (Diligence)、Michael Lewellen (OpenZeppelin)、Luis Lubeck (Hacken)、Dominik Muhs、Anton Permenev (ChainSecurity)、
Juliano Rizzo (Coinspect)、Gernot Salzer、Clara Schneidewinde、Tobias Vogel (Diligence)、Morgan Weaver (OpenZeppelin)

版权声明

© 2020-2025 企业以太坊联盟 (Enterprise Ethereum Alliance)。

摘要

本文档定义了EEA EthTrust认证的要求,该认证是一组证明智能合约已通过审查且未包含特定安全漏洞的认证。

文档状态

本节描述了本文档发布时的状态。新文档可能会取代本文档。
本文档是​​EEA EthTrust安全等级规范第3版​​,由EthTrust安全等级工作组开发,企业以太坊联盟公司发布。
本草案内容已获EEA批准发布。工作组预计在发布时将继续工作,并在2026年下半年最终确定并发布新版本以取代本文档。
本规范由企业以太坊联盟公司根据Apache许可证2.0版授权。除非EEA明确书面授权,否则您只能根据该许可证的条款使用本规范。
除非适用法律要求或书面同意,本规范按”原样”分发,不提供任何明示或暗示的担保或条件。
对本规范的反馈可直接发送至编辑EEA Editor,或作为问题提交至EthTrust-public GitHub仓库。

​​GitHub Issues​​是讨论本规范的首选方式。

1. 简介

本节为非规范性内容。
本文档定义了为用Solidity编写的智能合约授予EEA EthTrust认证的要求。
EEA EthTrust认证是安全审查员的一项声明,表明根据审查员针对特定要求的评估,​​被测代码​​不易受到一系列已知攻击或预期操作失败的影响。

没有任何安全审查可以保证智能合约免受所有可能的漏洞攻击,如第三章. 安全考虑中进一步解释。然而,根据本规范中的要求审查智能合约,可以提供保证,确保其不易受到一组已知的潜在攻击。

这种保证不仅基于审查员的声誉,还基于来自许多竞争组织的众多安全专家在EEA内的协作,确保本规范定义了对真实且重要的一组已知漏洞的防护。

1.1 如何阅读本规范

本节介绍如何理解本规范,包括示例和要求的约定、核心概念、参考文献、说明性章节等。

1.1.1 文档概述

文档结构大致如下:
​前置内容​​:文档基本信息(作者、版权等)
​符合性章节​​:声明符合本规范的含义和形式
​​安全考虑​​:与智能合约相关的关键安全概念介绍
​测试方法​​:与本规范相关的不同测试方法介绍
​​EthTrust安全等级​​:文档核心部分。按等级和主题分组的安全审查要求
​​附加信息​​:

  • 术语表
  • 要求和推荐实践的摘要
  • 致谢
  • 自上一版本以来的重大变更摘要
  • 已从早期版本中移除的需求列表

​​参考文献​​:扩展阅读,包括必要的规范性引用和说明性引用

本规范附有​​检查清单​​,以表格形式列出所有要求。熟悉本规范的开发者或审查员可使用该清单快速回顾每个单独要求并跟踪测试状态。如有任何差异,以本规范文档中的规范性文本为准。

1.1.2 排版约定

​​要求​​的结构和格式详见§1.1.3。
部分位置提供​​示例​​。这些示例不是要求,也不具有规范性。
示例通过带边框的背景和标题进行区分,如下所示:

​​示例1:示例的示例​​ 示例中可以包含行内代码如code(),也可以包含代码块:
1
2
3
4
5
6
// SPDX-License-Identifier: MIT
pragma solidity 0.8.0;

contract HelloWorld {
string public greeting = "Hello World";
}

部分示例展示​​易受攻击的代码​​或​​不应采取的做法​​。
将此类示例复制到生产代码中是非常危险的行为。这些示例会标记为警告:

警告
1
2
​示例2:问题示例 - 请勿复制​​
部署未经安全审查的代码, 这是危险行为。

​​术语定义​​的格式为​​加粗​ ​,后续对已定义术语的引用会以链接形式呈现​。
对其他文档的引用以方括号内链接形式给出,如[CWE]
对要求的引用以安全等级开头:[S]、[M]或[Q],对推荐实践的引用以[GP]开头。它们会包含要求或实践名称,并以加粗链接形式呈现:

​​示例3​
​ 示例链接:[M][记录特殊代码用途](https://entethalliance.org/specs/ethtrust-sl/v3/#req-2-documented)。

在语句或要求中引入并后续描述的​​变量​​格式为斜体 var
偶尔出现的说明性注释(如下所示)不具有规范性,不构成正式要求:

注意
1
2
​​注释用于说明
注释内容旨在提供有用信息,但不构成要求。

1.1.3 如何阅读要求

本文档的核心是​​共同定义了EEA EthTrust认证的要求。
要求包含:

  • 安全等级([S]、[M]或[Q])
  • 对应名称
  • 链接(以”🔗”标识)
  • 满足要求必须实现的声明

工作组确保本编辑草案中要求的URL始终指向工作组编辑草案中的最新版本要求(可能代表未完成的进展)。
部分相同安全等级的要求会分组在小节中,因为它们与特定主题或潜在攻击领域相关。
要求后跟解释,包括:

  • 该要求的重要性
  • 测试方法
  • 覆盖要求和相关要求的链接
  • 测试用例
  • 其他有用信息的链接
    除要求外,本文档还包含​​§5.4 推荐良好实践​​,格式类似但安全等级标记为”[GP]”。
    为符合规范,无需实施这些实践,但若谨慎实施可提高智能合约安全性。

以下要求示例:

​​示例4:简单要求​​
1
2
3
4
5
[S] 编译器漏洞SOL-2022-5于.push() 🔗
被测代码若:
- 从calldata或内存复制字节数组,且其大小不是32字节的倍数
- 包含对结果数组执行空.push()指令
则​​必须不使用​​早于0.8.15的Solidity编译器版本。
在Solidity编译器0.8.15之前,复制长度非32字节倍数的内存或calldata可能暴露超出复制范围的数据,这些数据可通过assembly{}观察到。 另见2022年6月15日安全警报和相关要求[M] assembly{}中的编译器漏洞SOL-2022-5。

这是一个安全等级[S]要求,[S]前缀表示其等级。
其名称为”编译器漏洞SOL-2022-5与.push()”。
编辑草案中的URL(通过”🔗”链接)为:
https://entethalliance.github.io/eta-registry/security-levels-spec.html#req-1-compiler-SOL-2022-5-push

要求声明为:
“被测代码若…必须不使用早于0.8.15的Solidity编译器版本。”
要求后是对相关漏洞的简要解释和其他讨论链接。

注意
良好实践的格式与要求相同,但等级标记为[GP]。如§5.4所述,满足这些实践并非必须,也不会单独改变对本规范的符合性。
1.1.3.1 覆盖要求

部分要求的声明包含替代条件,由关键词unless引入,标识一个或多个​​条件可以覆盖当前要求​​。
这些是更高安全等级的要求,若被测代码不直接满足较低等级要求,可通过满足这些覆盖要求来实现符合性。

某些情况下需满足多个覆盖要求才能覆盖原要求,此时这些要求被称为​​覆盖要求集合​​。
必须满足集合中所有要求才能视为覆盖原要求。

许多情况下,一个要求可能有多个覆盖要求或覆盖要求集合可供选择以满足该要求。
例如,有时可通过以下方式满足安全等级[S]要求:

  • 直接满足
  • 满足安全等级[M]的覆盖要求集合
  • 满足安全等级[Q]的覆盖要求集合
  • 覆盖要求支持对常见简单情况进行更简单的测试。

对于使用需要额外谨慎处理功能的更复杂被测代码,它们确保此类使用经过适当检查。

典型的安全等级[S]要求中,覆盖要求适用于相对罕见的情况或自动化系统通常无法验证被测代码是否满足要求的情况。
通过进一步验证适用的覆盖要求,可确定被测代码是否正确使用功能,从而通过安全等级[S]要求。

若被测代码不满足某要求且无适用的覆盖要求,则该代码不符合EEA EthTrust认证条件。
但即使对于此类情况,请注意推荐实践[GP]尽可能满足更多要求;满足本规范中的任何要求都将提高被测代码的安全性。

在以下要求中:

  • 安全等级为”[S]”
  • 名称为”禁用tx.origin”
  • 覆盖要求为”[Q] 验证tx.origin用法”
1
2
3
​​示例5:带覆盖要求的示例​​
[S] 禁用tx.origin🔗
被测代码​​不得​​包含tx.origin指令,除非满足覆盖要求[Q] 验证tx.origin用法。

不包含tx.origin指令的要求可通过自动化验证。
满足安全等级[Q]覆盖要求[Q] 验证tx.origin用法的被测代码视为符合此安全等级[S]要求。

作为其他要求的覆盖要求或属于覆盖要求集合的要求会明确说明:

1
2
3
4
5
6
​​示例6:覆盖要求示例​​
[M] 禁用非必要Unicode控制符🔗
被测代码​​不得​​使用Unicode方向控制字符,除非:
- 它们对正确渲染文本是必要的
- 渲染结果不会误导读者
这是[S] 禁用Unicode方向控制字符的覆盖要求。
1.1.3.2 相关要求

许多要求有​​相关要求​​,即解决主题相关问题的其他要求。
提供这些链接作为有用信息。与覆盖要求不同,满足相关要求不能替代满足特定要求来实现符合性。

1.2 为什么需要认证合约?

本节为非规范性内容。
以太坊上支撑去中心化应用的许多智能合约已被发现存在安全问题,而目前在发起交易前往往难以实际评估某个地址或合约的安全性。特别是在DeFi领域,用户快速批准代币合约交易、兑换代币和为资金池添加流动性的操作中,有时会忽略安全检查。

要让企业信任以太坊作为关键数据存储或大额资本转移的交易层,需要明确的信号表明合约已通过适当的安全审计。在区块链开发背景下,生产部署前的早期审查尤为重要,因为尝试在部署后更新或修补智能合约所需的时间、精力、资金和/或信誉成本通常远高于其他软件开发场景。

本智能合约安全标准旨在提升对智能合约安全审计质量的信心,从而增强整个以太坊生态系统的信任度。认证不仅为智能合约的实际或潜在用户提供价值,也为可能受智能合约使用或滥用影响的其他利益相关方提供价值。通过EEA EthTrust认证限制暴露于某些已知弱点,这些利益相关方可以受益于风险降低和对被测代码安全性的信心提升。

需要注意的是,这种保证并不完整:例如它依赖于出具认证的审计师的能力和诚信。专业声誉可能基于被测代码的后续表现而变化,特别是当被测代码变得足够引人注目以至于人们有更大的动机去探索剩余的漏洞。

最后,当其他方(包括直接竞争对手)完成认证流程时,智能合约开发者和生态系统利益相关方也会获得价值,因为这意味着其他合约不太可能产生与利用相关的负面新闻,这些新闻可能导致商业领袖、潜在客户/用户、监管机构和投资者对以太坊技术产生不安全或高风险的负面看法。

智能合约安全认证的价值在某些方面类似于适用于飞机部件的认证流程。最直接的是,它通过提供最低质量保证,帮助部件制造商和集成商降低风险。间接而言,这些流程显著减少了航空事故和坠机,拯救生命并赢得监管机构和考虑行业整体安全与风险的客户的信任。许多安全认证流程最初是作为制造商创建的自愿程序,或代表重要市场份额的客户联盟指定和要求开始的。在证明其价值后,其中一些认证流程现在已成为法律要求以保护公众。

我们希望认证流程的价值能激励其频繁使用,并推动开发使评估过程更简单、更便宜的自动化工具。随着新的安全漏洞、本规范中的问题以及实施挑战的发现,我们希望它们能带来反馈并增加参与企业以太坊联盟EthTrust安全等级工作组或其继任者的积极性,这些工作组负责开发和维护本规范。

1.3 开发安全的智能合约

本节为非规范性内容。

本规范要求检查的安全问题对智能合约开发者(尤其是这个快速发展的领域的新手)来说并不总是显而易见的。通过让自有代码完成认证流程(即使没有潜在客户要求),智能合约开发者可以在部署前发现代码中存在的已知弱点并进行修复。

开发者应尽可能使代码安全。与其仅瞄准特定安全等级的符合性要求,不如确保代码尽可能多地实现本规范的要求(根据[GP]尽可能满足更多要求),这有助于确保开发者已考虑本规范解决的所有漏洞。

除了明显的声誉收益外,开发者还将从这一过程中学习,提高对潜在弱点的理解,从而完全避免这些弱点。

对开发和部署智能合约的组织而言,这一流程减少了安全审查所需的工作量,并降低了对其信誉、资产和其他资本的风险。

1.4 漏洞反馈与新漏洞

工作组欢迎对本规范的反馈:实施经验、改进清晰度的建议,或对特定章节或要求难以理解的疑问。

我们特别希望获得关于使用标准机器可读格式进行有效符合性声明的反馈,包括这种格式是否适合存储在区块链上,以及其他用例。

EEA成员可通过加入工作组提供反馈。任何人都可通过Ethtrust-public Github仓库或发送邮件至EEA Editor提供反馈,邮件将被转发给工作组。

我们预计本规范发布后将发现新的安全漏洞。为确保将它们纳入修订版本,我们欢迎相关通知。EEA已创建特定电子邮件地址接收新安全漏洞通知:security-notices@entethalliance.org

发送至该地址的信息应足以识别和纠正描述的问题,并应包括对该问题的其他讨论的引用。这些信息将由EEA工作人员评估,然后转发给工作组解决问题。

当这些漏洞影响Solidity编译器,或建议修改编译器以帮助缓解问题时,还应按照solidity-reports中的描述通知Solidity开发社区。

2. 符合性

2.1 符合性声明

本规范中的关键词”可以(MAY)”、”必须(MUST)”、”不得(MUST NOT)”、”建议(RECOMMENDED)”和”应当(SHOULD)”应按照BCP14 RFC2119RFC8174中的说明进行解释,且仅当这些词汇以全大写形式出现时才具有此特定含义。

本规范定义了若干要求。如§1.1.3所述,每个要求都有安全等级([S]、[M]或[Q])和被测代码必须满足的声明。为获得特定安全等级的EEA EthTrust认证,被测代码必须满足该安全等级的所有要求,包括更低安全等级的所有要求。某些要求可以直接满足,也可以通过满足一个或多个覆盖要求来视为满足。

本文档不产生任何方的主动合规义务,但潜在客户或投资者通过合同谈判或其他流程可能会产生合规要求。

§5.4推荐良好实践章节包含更多建议。虽然它们格式与要求类似,但以”[GP]”标记开头。不要求测试这些内容;但建议谨慎实施和测试。

请注意,§5.4推荐良好实践可以增强安全性,但在某些情况下,不完整或低质量的实施可能会降低安全性。

2.2 谁可以提供EEA EthTrust认证?

本版本规范未对谁能执行审计和提供EEA EthTrust认证做出任何限制。没有为出具认证的审计师或工具定义认证流程。这意味着审查员关于执行准确测试的声明是由他们自己做出的。对于提供EEA EthTrust认证第2版认证的任何人,始终存在欺诈、虚假陈述或无能的可能。

注意
原则上任何人都可以提交智能合约进行验证。但提交者需要注意使用限制,如版权条件等。此外,在智能合约开发控制有限的情况下,证明满足某些要求可能更加困难。工作组期望其成员(他们编写了本规范)保持高标准的诚信,并熟悉本规范,同时注意到还有许多其他人也这样做。工作组或EEA可能会为EEA EthTrust安全等级规范后续版本开发审计师认证计划。

2.3 识别认证内容

EEA EthTrust评估针对被测代码,被测代码是指智能合约或几个相关智能合约的Solidity源代码,以及使用指定参数编译代码生成的字节码。如果被测代码被划分为可部署在不同地址的多个智能合约,则称为合约集合。

3. 安全考虑

3.1 智能合约的上下文 - 更广泛的考虑

信息系统安全是本规范主要的工作领域。任何中等复杂度的系统都存在固有风险。本规范描述了以太坊智能合约中安全问题的测试。然而,不存在完美的安全性。EEA EthTrust认证意味着至少已对智能合约执行了定义的最小检查集。这并不意味着被测代码绝对没有安全漏洞。新的安全漏洞会不时被发现。手动审计程序需要技能和判断力。这意味着始终存在审查中未注意到漏洞的可能性。

3.2 可升级合约

以太坊中的智能合约默认是不可变的。然而,在某些场景下需要修改它们,例如添加新功能或修复错误。可升级合约是通过启用对固定地址执行的代码的更改来满足这些需求的任何类型的合约。可升级合约的一些常见模式使用代理合约:用户直接与之交互的简单包装器,负责将交易转发到另一个合约(本文档中称为执行合约,也称为逻辑合约),该合约包含实际实现智能合约行为的代码。执行合约可以被替换,而作为访问点的代理合约永远不会更改。这两个合约在代码无法更改的意义上仍然不可变,但一个执行合约可以与另一个交换。因此,代理合约可以指向不同的实现,从而”升级”软件。这意味着遵循此模式使合约集合可升级的合约通常不能被视为不可变的,因为代理合约本身可以将调用重定向到新的执行合约,这些执行合约可能不安全或恶意。通过满足本规范中关于访问控制的要求以限制部署新执行合约的升级能力,并按照 [Q]按文档实现 记录升级模式并遵循该文档,被测代码的部署者可以证明可靠性。

通常,代理合约的EEA EthTrust认证不适用于可升级合约的内部逻辑,因此在通过代理合约升级到新执行合约之前需要对新执行合约进行认证。这种核心结构有多种可能的变体,例如包含多个执行合约的合约集合。在称为变形升级的攻击中,一系列智能合约用于说服人们(例如DAO中的投票者)批准部署某段代码,但链中的一个代理合约被更新为部署不同的恶意代码。其他模式依赖于使用CREATE2指令在已知地址部署智能合约。目前可以使用selfdestruct()方法删除该地址的代码,然后向该地址部署新代码。这种可能性有时用于节省Gas费用,但也用于变形升级攻击中。

3.3 预言机

以太坊网络的常见功能是使用预言机:可以提供来自链上或链下数据的信息的功能。预言机解决了一系列问题,从提供随机数生成到资产数据,管理流动性池的操作,以及实现对天气、体育或其他特殊兴趣信息的访问。预言机在DeFi和游戏中被大量使用,其中资产数据和随机化是协议设计的核心。

该规范包含检查智能合约是否足够强大以适当处理任何信息返回的要求,包括可能被故意制作用于预言机特定攻击的畸形数据的可能性。虽然预言机的某些方面在本规范考虑范围内,但预言机仍可能提供错误信息,甚至主动产生有害的虚假信息。两个关键的考虑因素是数据损坏或被操纵的风险,以及预言机故障的风险。与这些考虑因素相关的漏洞 - 过度依赖TWAP(Time-Weighted Average Price),以及对预言机故障的不安全管理 - 已经反复发生,导致各种DeFi协议损失数百万美元的价值。虽然有许多高质量和可信的预言机可用,但即使使用合法数据也可能遭受攻击。当调用预言机时,需要检查接收到的数据是否过时,以避免抢先交易攻击。即使在非DeFi场景中,例如随机性来源,通常也需要为每笔交易重置数据源,以避免下一笔交易的套利。

定价预言机的常见策略是提供时间加权平均价格(称为TWAP)。这在一定程度上防止了闪电贷攻击等突然价格飙升,但代价是提供过时信息。仔细选择时间窗口很重要:当时间窗口太宽时,它不会反映波动的资产价格,给套利者留下机会。然而,资产的”瞬时”价格通常不是一个好的数据点:它是预言机数据中最容易被操纵的部分,而且几乎总是在交易执行时就已经过时。使用整理各种来源数据、从数据中清除异常值并受到社区好评的预言机更有可能可靠。如果预言机是链下的,它反映的是链上数据是过时的还是可靠和准确地反映链下数据是一个重要的考虑因素。即使使用选择良好的TWAP的预言机也可以操纵流动性池或其他DeFi结构,特别是通过利用闪电贷和闪电互换廉价筹集资金。如果目标操纵的资产流动性不足,则可能使其容易受到攻击者仅持有相对少量流动性的影响而导致大幅价格波动。

在使用预言机时,第二个重要的考虑因素是如何优雅地处理故障情况。如果预言机停止返回数据,或者突然返回一个极不合理的数值,会发生什么?至少有一个协议因此遭受了损失:在极端价格崩盘的罕见情况下,价格并未真正跌至零,而是“挂”在了一个设定的最小值上,使得一些囤积了接近零价格资产的交易者可以将其高价卖回协议。将最小值或最大值硬编码在系统中,可能导致价格难以真实反映市场情况。

3.4 外部交互与重入攻击

依赖外部代码的代码可能引入多个攻击向量。这包括外部依赖包含恶意代码或通过安全漏洞受到恶意操纵的情况。然而,未能充分管理外部调用的可能结果也会引入安全漏洞。

以太坊智能合约中最常被引用的漏洞之一是重入攻击。这些攻击允许恶意合约在原始合约的函数调用完成之前回调到调用它的合约中。这种效果导致调用合约以意外的方式完成其处理,例如,对状态变量进行意外更改。

虽然Check-Effect-Interactions实现模式提供了关键保护,但新兴的跨合约交互模式可能需要额外的保障措施。定期审查交互模式有助于识别新的重入向量。

只读重入攻击发生在视图函数读取随后将被更改的状态时。这些攻击特别危险,因为此类函数通常缺乏保障措施,因为它们不修改合约的状态。然而,如果状态不一致,可能会报告不正确的值。这种欺骗可能导致其他协议读取不准确的状态值,从而可能导致意外操作或结果。这个问题可能影响依赖这些视图函数准确报告状态的其它合约,以及被重入的合约本身。因此,调用智能合约的第三方以及由合约集合组成的协议可能容易受到只读重入的影响。

示例7:Rari协议只读重入攻击​​
在Rari协议攻击中,攻击者通过大额闪电贷存入资金,在借款调用期间触发Comptroller合约的`exitMarket()`函数。该函数读取cETH合约状态时发生只读重入。由于cETH合约尚未记录借款状态,攻击者得以赎回初始存款并保留借款资金。详见[certik-rari](https://entethalliance.org/specs/ethtrust-sl/v3/#bib-certik-rari)分析报告。

3.5 签名机制

本规范中的一些要求涉及可延展签名。这些是根据方案创建的签名,给定消息和签名,可以高效计算不同消息的签名 - 通常是以特定方式转换的消息。虽然这种签名方案允许有价值的用例,但如果不谨慎使用可能导致漏洞,这就是为什么本规范试图适当限制其使用。同样,对于使用可延展输入创建的可延展签名消息,哈希冲突可能发生。

本规范中的其他要求与利用用于创建签名消息的输入中的模糊性进行攻击有关。当签名消息不包括关于其预期使用位置、时间、次数等的足够识别信息时,消息签名可能被用于(或重复用于)非预期功能、合约、链或时间。

有关此主题的更多信息以及潜在利用,请参见chase

3.6 Gas与Gas价格

Gas攻击是故意滥用以太坊用于调节计算能力消耗的Gas机制,以防范意外或不利结果(如拒绝服务攻击)。由于以太坊设计将Gas机制作为调节功能,仅检查交易是否有足够的Gas是不够的;检查Gas攻击需要考虑被测代码实现的目标和业务逻辑。

Gas虹吸是另一种滥用以太坊用于调节计算能力消耗的Gas机制的行为,攻击者从易受攻击的合约中窃取Gas,要么拒绝服务,要么为自己谋利(例如铸造Gas代币)。与Gas攻击类似,检查Gas虹吸需要仔细考虑被测代码实现的目标和业务逻辑。

Gas代币在铸造时使用Gas,在销毁时释放略少的Gas,前提是EVM退还足够数量的Gas以清除状态。当Gas价格低时铸造的Gas代币可以在Gas价格高时燃烧以补贴以太坊交易。在以太坊主链上,随着2021年8月部署EIP-3529的伦敦硬分叉,Gas退款被移除,实际上禁用了Gas代币。此外,以太坊网络升级的常见功能是更改特定操作的Gas价格。EEA EthTrust认证仅对指定的EVM版本有效;对其他EVM版本无效。因此,重新检查代码以确保其安全属性在网络升级中保持不变或采取补救措施非常重要。

3.7 MEV(恶意提取价值)

MEV(Maliciously Extracted Value)在本文档中表示”恶意提取价值”,指的是区块生产者或区块链的其他参与者通过恶意重新排序交易或抑制交易,或通过提出交易或采取其他行动获取非预期利益(即窃取)的可能性。

示例8:MEV攻击实例​​
当智能合约承诺奖励首个回答问题的交易时,区块生产者可以窃取其他交易中的答案,并丢弃包含该答案的所有其他交易。
注意
MEV一词通常扩展为“矿工提取价值”(Miner Extracted Value),有时也扩展为“最大可提取价值”(Maximum Extractable Value)。通常,如上例所示,区块生产者可以充分利用漏洞。 然而,即使没有自己生产区块,其他参与者也可能利用MEV。 此外,部分价值提取本质上是区块生产者利用已知的套利机会,以提供一个更可预测的高效市场。
一些MEV攻击可以通过仔细考虑交易中包含的信息(包括合约所需的参数)来防范。其他缓解策略包括针对排序攻击的保护措施。以太坊基金会维护有关MEV的信息资源集[EF-MEV](https://entethalliance.org/specs/ethtrust-sl/v3/#bib-ef-mev)。

3.8 排序攻击

各种攻击与恶意重新排列区块中的交易有关,例如通过重新排序、审查或插入特定交易。虽然这类攻击的主要动机是促进MEV攻击,但它们也可用于为其他类型的攻击创造条件。

警告
示例9:排序攻击
投票合约的设置阶段有时可能被恶意抑制或抢跑投票选项提案所利用,以限制投票阶段符合条件的候选人数量。区块提议者可以在知道他们将产生一系列区块时恶意安排此类投票初始化。

排序攻击有多种类型:

  • 审查攻击, 区块处理者主动抑制提议的交易,为自己谋利。

  • 抢跑, 基于在添加到区块之前可见的交易,允许恶意参与者提交替代交易,挫败原始交易的目的。

    示例10:抢跑攻击策略
    在一个旨在认证原创著作权的系统中,恶意参与者利用著作权声明中的信息伪造竞争性声明,并率先将伪造声明加入区块,从而为其虚假主张作者身份提供依据。 若重复实施此类攻击,将成为针对该服务本身的有效拒绝服务(DoS)攻击手段。
  • 尾随, 类似于抢跑,但攻击者将他们的交易放在被攻击的交易之后。

  • 三明治攻击, 攻击者将受害者的交易不理想地放在另外两个交易之间。

    示例11:三明治攻击策略​​ 攻击者构造代币买入交易插入受害者买单之前推高价格,再在抬高的价位插入对应卖单,通过这种"夹心"操作实现无风险套利。
注意
与利用MEV一样,区块生产者处于利用任何排序漏洞的最佳位置。如果他们提前知道他们将在未来产生特定区块,这一点尤其成立。例如参见[futureblock](https://entethalliance.org/specs/ethtrust-sl/v3/#bib-futureblock)或[postmerge-mev](https://entethalliance.org/specs/ethtrust-sl/v3/#bib-postmerge-mev)。

实现等级[Q]要求 [Q]对敏感操作使用时间锁延迟 可以防止排序攻击影响敏感操作的执行。其他缓解策略包括使用哈希承诺方案hash-commit、批量执行或使用第2层EEA-L2链进行处理。

3.9 源代码、编译指示和编译器

本规范版本要求编译的字节码以及构成被测代码的是Solidity源代码。Solidity在很大程度上是以太坊智能合约最常用的编程语言,要求Solidity源代码的好处包括简化许多测试,并且有大量针对Solidity源代码的安全研究。

Solidity允许源代码使用编译指示语句指定使用的Solidity编译器版本。本规范不要求任何特定的Solidity编译器版本,只要其不低于0.3.0,但在安全等级[Q]下,仅允许EEA EthTrust认证用于一组有限的Solidity编译器版本,已知这些版本的Solidity编译器在相同选项下从给定源代码生成相同的字节码。

要求合约只是Solidity源代码有一些缺点。最明显的是某些代码不是用Solidity编写的。不同的语言有不同的特性,通常支持不同的编码风格。也许更重要的是,这意味着用Solidity编写的已部署合约不能直接测试,除非有人提供源代码。引入源代码读取的另一个重要限制是它容易受到同形文字攻击(Homoglyph Attacks),其中看起来相同但不同的字符(如拉丁字母”p”和西里尔字母”р”)可以欺骗人工阅读源代码的人,掩盖恶意行为。还有相关的攻击使用诸如Unicode方向控制字符之类的功能,或利用组合字符的不一致规范化来实现相同类型的欺骗。

3.10 合约部署

本规范主要解决智能合约代码中出现的漏洞。然而,需要注意的是,智能合约的部署通常是协议操作的关键要素。智能合约安全的某些方面主要取决于被测代码的部署方式。即使经过审计的合约如果部署不当也可能容易被利用。

为特定区块链编写的代码可能依赖于该区块链可用的功能。当代码部署到兼容但不同的链时,功能差异可能暴露漏洞。对于部署到使用EVM补丁分叉的区块链或平行链的任何合约,在虚拟机级别可能不再适用常见的安全假设。首先将EEA EthTrust认证的合约部署到每个链的测试网,并进行彻底的渗透测试是有价值的。

特别令人担忧的是可升级合约的问题,以及部署中具有初始化函数的任何合约。许多合约因意外保留其初始化函数不受保护,或在部署中未在同一交易中调用初始化函数的非原子部署而被黑客攻击。这种情况容易受到抢跑攻击,并可能导致合约被恶意方接管,以及资金被盗或丢失。在与合约部署相同的交易中初始化合约可降低恶意行为者控制合约的风险。

此外,在构造函数和初始化函数中为msg.sender或其他变量分配访问角色对部署的影响需要仔细考虑。这在§5.3.2访问控制要求中进一步讨论。存在专门用于安全代理使用和安全合约部署的多个库和工具。从命令行工具到库再到复杂的基于UI的部署工具,存在许多解决方案来防止不安全的代理部署和升级。对给定合约的初始化函数使用访问控制,并限制在部署时或部署后可以调用初始化函数的次数,可以增强协议本身及其用户的安全性和透明度。此外,禁用重新初始化执行合约能力的函数可以防止以后的攻击或事故。

虽然本规范不要求被测代码已部署,但某些要求在代码部署到区块链时更容易测试,或者在某些情况下可能只能”在现场”彻底测试。

3.11 部署后监控

虽然智能合约部署后的监控超出了本规范的正式范围,但它是智能合约安全的重要考虑因素。新的攻击技术会不时出现,某些攻击只能通过实时实施的主动措施来防范。对链上活动的监控可以帮助在攻击造成不可挽回的损失之前检测到它们。监控(基于自动化数据集)可以识别已在其他地方发生的攻击,甚至是在其他区块链上。自动化监控可以促进快速响应,生成警报或自动启动操作,提高合约的安全性,否则当安全响应延迟甚至几个区块时,合约可能会受到损害。然而,区分攻击和个人异常行为可能很困难。纯粹依赖自动化监控会使区块暴露于恶意行为者故意触发自动安全响应以致破坏区块链或项目的风险,类似于拒绝服务攻击。

3.12 网络升级

EVM或以太坊虚拟机作为以太坊网络的分布式状态机,计算由交易引起的状态变化。EVM维护简单以太转账以及更复杂的智能合约交互的网络状态。换句话说,它是运行智能合约代码的”计算机”(尽管实际上是软件)。以太坊社区不时实施网络升级,有时也称为硬分叉。这是对以太坊的向后不兼容更改。因为它们通常会更改EVM,所以以太坊主网网络升级通常对应于EVM版本。

网络升级可能或多或少影响以太坊的各方面,包括更改EVM操作码或其Gas价格、更改添加块的方式或支付奖励的方式等许多可能性。由于不保证网络升级向后兼容,较新的EVM版本可能以意外的方式处理字节码。如果网络升级更改EVM以修复安全问题,考虑该更改很重要,遵循该升级是一个好习惯。因为对本规范的符合性声明仅对特定的EVM版本有效,所以网络升级可能意味着需要更新的审计以维护对当前以太坊网络有效的EEA EthTrust认证。网络升级通常只影响少数功能。这有助于限制升级后审计代码所需的努力:通常不会有影响被测代码的更改,或者审查受网络升级影响的唯一部分的一小部分就足以更新EEA EthTrust认证。

3.13 组织与链下安全态势

智能合约安全不仅限于代码,还涵盖组织流程和链下基础设施。全面的安全策略需要解决协议管理的技术和运营两个方面。

运营安全措施包括:

  • 关键密钥存储的硬件安全模块(HSM)
  • 分布式密钥持有者的多重签名方案
  • 所有团队成员的定期安全培训
  • 安全开发环境协议

基础设施安全包括:

  • 受保护的部署基础设施
  • 安全通信渠道
  • 访问控制系统
  • 网络安全监控

监控和响应包括:

  • 实时交易监控
  • 自动警报系统
  • 事件响应程序
  • 紧急关闭能力

旨在遵循最佳实践的组织将实施:

  • 定期对链上和链下系统进行安全评估
  • 记录明确角色和职责的事件响应计划
  • 基础设施的定期渗透测试
  • 安全意识培训计划
  • 访问控制审查和更新
示例12:安全运营中心监控项​​ 健全的安全运营中心(SOC)应监控:非常规交易模式、可疑管理操作、基础设施安全警报、智能合约异常行为。

3.14 预防链上对抗条件

智能合约在高度对抗的环境中运行,其中网络条件、外部数据源和经济激励可能被恶意行为者操纵。在部署前模拟这些攻击场景对于识别在标准测试条件下可能不会显现的漏洞至关重要。
需要模拟的关键对抗场景包括:
网络操纵:

  • 极端Gas价格波动
  • 故意造成网络拥堵
  • 矿工策略性交易排序
  • 区块时间戳操纵

预言机攻击:

  • 通过闪电贷操纵价格反馈
  • 延迟或过时数据场景
  • 多种预言机故障模式
  • 跨链预言机不一致

经济战:

  • 极端资产价格波动
  • 流动性池操纵
  • 代币经济攻击
  • 套利利用

治理利用:

  • 代币投票操纵
  • 提案泛滥攻击
  • 投票期的定时攻击
  • 恶意参数更新

标准测试环境通常无法捕捉这些对抗条件之间的复杂交互。在受控测试下看起来安全的协议,当多个攻击向量组合或经济激励足够大时,可能隐藏关键漏洞。

注意
动态测试环境: 测试这些场景需要能够模拟复杂市场条件和参与者行为的动态环境。简单的单元测试或静态分析工具无法充分建模这些对抗情况。

通过在部署前建模这些场景,开发者可以:

  • 识别经济模型中的边缘情况
  • 验证断路器机制
  • 测试紧急关闭程序
  • 检查治理保障措施
  • 评估压力下的协议弹性

4. 测试方法

4.1 单元测试

单元测试实践基于多个独立测试用例,每个测试用例验证特定需求。这种方法可融入测试驱动开发(TDD)流程,即在编写代码时同步构建测试用例集,确保变更不会引入已解决问题或新问题。

测试覆盖率是评估单元测试价值的关键指标,意味着不仅需要覆盖每个需求,还需覆盖触发该需求的所有可能路径。例如,测试tx.origin指令时,需同时测试其在Solidity代码和assembly{}中的使用场景,否则无法满足特定需求的全覆盖测试。

典型的单元测试自动化方案包括构建测试工具链,确保无论智能合约代码或运行环境发生变更(如集成新服务或系统),测试用例都能自动执行。

4.2 静态分析

静态分析指直接检查被测代码以识别潜在问题。本规范中,安全等级[S]的所有问题均可通过自动化静态分析发现。企业以太坊联盟(EEA)的EthTrust安全等级工作组已开始收集相关工具信息,并构建测试用例库验证工具准确性。

人工静态分析同样重要,该方法依赖专家的经验和判断力识别代码编写方式可能导致的问题。工作组认为人工静态分析足以验证被测代码是否符合本规范安全等级[M]的要求。

4.3 模糊测试

模糊测试是通过向合约输入多样化数据来暴露缺陷的自动化测试方法。其有效性很大程度上取决于语料库质量——即测试输入数据集。维护语料库时需平衡代码覆盖率与效率,剔除冗余或重复输入。

模糊测试主要分为三类:
​​黑盒测试​​:将智能合约视为不透明目标,仅通过外部接口测试,适用于快速发现典型使用场景中的基础错误。
​​白盒测试​​:基于完整源代码和执行路径可见性,使用符号执行等技术指导输入生成,能更精准定位复杂逻辑缺陷。
​​灰盒测试​​:结合前两种方法要素,通过有限插装或启发式方法生成测试输入,在检测广度与深度间取得平衡。

4.4 变异测试

变异测试通过向源代码注入人工缺陷(变异)来评估测试用例的有效性。若测试用例能检测到变异(即”杀死”变异体),说明测试充分;否则表明存在覆盖缺口。

智能合约适用的变异操作包括:
状态变量突变: 修改状态变量声明和作。
算术突变: 改变算术运算以测试数值计算。示例包括:

  • 替换运算符 ( , -, *, /)
  • 修改边界条件
  • 引入溢出/下溢条件
    控制流突变: 更改执行路径,例如:
  • 反转条件语句
  • 修改循环条件
  • 更改函数修饰符
    访问控制突变: 不同的安全关键型权限检查,例如:
  • 删除所有权检查
  • 更改基于角色的权限
  • 修改身份验证逻辑
    关键指标是变异分数(被杀死的变异数/非等价变异总数)。虽然100%的分数难以实现,但对核心合约组件应保持高分。该技术通过识别等价变异(不影响行为的变异)来优化测试效率,可作为模糊测试的补充。

4.5 符号执行

符号执行通过追踪符号值(类似代数中的未知变量)而非具体数值来分析程序。该方法能推导出可能输出结果的约束条件,用于检测潜在漏洞,同时可识别死代码、未使用变量等问题。详见符号执行权威指南WSE

4.6 形式化验证

形式验证是一系列以数学方式证明代码某些属性的技术。它已用于嵌入式系统等应用。形式验证在智能合约中有很多用途,例如测试活跃性、高级别的协议不变量以确保安全性,或者证明程序执行的更窄、更具体的属性。

在形式验证中,创建智能合约预期或期望结果的正式(符号或数学)规范,从而能够对协议的正确性进行正式的数学证明。为此,智能合约本身通常被翻译成另一种语言。

存在多种用于创建形式验证证明的语言和程序,其中一些语言和程序的明确目的是使临时用户和非数学家更容易访问形式验证。请参阅 EF-SL了解一些示例。

如果做得正确,形式化验证可以保证模糊测试和静态分析等方法无法保证。但是,其准确性取决于正确建模测试代码,并选择适当的属性进行测试。这项任务通常需要大量的专业知识,如果模型不能准确反映原始测试代码的属性,则得出的结果也可能不适用。

许多智能合约的不可变性使得形式验证具有吸引力。

4.7 属性与不变量

基于属性的测试通过定义系统应满足的特性或不变量(在任何情况下都应保持的条件),自动生成测试场景。不变量测试是其子集,专门验证这些不变条件的保持性。

4.8 测试网部署

除了静态分析之外,许多测试方法都依赖于能够执行测试代码。一种常见的做法是将代码部署在测试网上,测试网是一个专门为测试而创建的区块链,已知该区块链包括可能包含安全漏洞的智能合约,并且底层加密货币和gas的成本为零或可以忽略不计。

5. EEA EthTrust安全等级

EEA EthTrust认证分为三个安全级别。安全级别描述了每个安全级别的认证的最低要求:[S]、[M] 和 [Q]。这些安全级别依次提供更强的保证,确保智能合约不存在特定的安全漏洞。

安全级别 [S] 的设计使得在大多数情况下,如果按照众所周知的模式使用Solidity的常见功能,测试代码可以通过自动化的“静态分析”工具进行认证。
安全级别 [M] 要求进行更严格的静态分析。它包括需要人工审核员确定是否有必要使用某个功能,或者关于代码安全属性的声明是否合理的要求。
安全级别 [Q] 提供了对测试代码实现的业务逻辑的分析,并且代码不仅没有表现出已知的安全漏洞,而且还正确地实现了它声称要执行的逻辑。
可选的§5.4推荐良好实践,如果正确实施,将进一步增强智能合约的安全性。但是,没有必要测试它们是否符合此规范。

注意
该方案已与“OWASP 应用程序安全验证标准”规范系列[ASVS]中使用的一致性方法进行了比较。存在一些明显的差异,这主要是由于ASVS旨在实现的普遍适用性与该规范非常精确地专注于测试用 Solidity 编写的以太坊智能合约的安全性之间的差异。

该规范解决的漏洞来自多个来源,包括 Solidity 安全警报solidity-alerts、智能合约弱点分类swcregistry、TMIO 最佳实践tmio-bp、安全咨询通知的各种来源、以太坊社区的讨论和研究人员介绍新发现的漏洞,以及工作组参与者的丰富实践经验。

5.1 安全等级[S]

EEA EthTrust认证在安全等级[S]下,旨在允许未经引导的自动化工具分析大多数合约的字节码和源代码,并确定它们是否符合要求。安全等级[S]的要求设计为可使用自动化静态分析进行测试。截至本规范版本,工作组已开始维护(工具注册表),其中列出了声称覆盖特定要求的工具,以及测试用例和在这些工具中运行它们的结果。更多信息请参见ET-tools

对于某些难以自动验证的情况,存在更高级别的覆盖要求,可以通过满足这些要求来实现合规性。为了获得EEA EthTrust认证的安全等级[S],被测代码必须满足所有安全等级[S]的要求,除非它满足适用的覆盖要求中的每一项要求。

1
2
[S]哈希编码必需包含chainid
被测代码必须按照[EIP-155]的建议,在交易哈希中纳入chainid值。

EIP-155描述了一种增强的哈希规则,通过在哈希中纳入链标识符。虽然这仅在存在唯一链标识符时才能防止重放攻击,但使用该机制可以提供一定程度的鲁棒性,并大大增加执行重放攻击的难度。

1
2
3
4
[S]禁用CREATE2
被测代码不得包含CREATE2指令,除非满足以下覆盖要求集:
- [M] 保护CREATE2调用
- [M] 记录特殊代码用途

CREATE2操作码提供了与尚未在链上存在但可能最终包含代码的地址进行交互的能力。虽然这对于部署和与合约的反事实交互很有用,但它允许调用尚未知或可能被修改的代码,这些代码可能由于错误或保护不足而变得恶意或不安全。

1
2
[S]禁用tx.origin
被测代码不得包含tx.origin指令,除非满足覆盖要求[Q]验证tx.origin用法。

tx.origin是Solidity中的一个全局变量,返回发送交易的账户地址。使用tx.origin的合约可能允许授权账户调用恶意合约,使恶意合约能够在非预期情况下通过授权检查。更好的做法是使用msg.sender进行授权。
参见swcregistry中的SWC-115获取示例。

1
2
[S]禁用精确余额检查
被测代码不得测试账户余额是否完全等于(即==)指定金额或变量值,除非满足覆盖要求[M]验证精确余额检查。

以账户余额作为某些操作的基础存在风险,包括意外接收以太币或其他代币,包括故意转移代币以使此类测试失败的MEV攻击。
参见相关要求:

  • [M] 随机性来源
  • [M] 不要滥用区块数据
  • [Q] 防范MEV攻击
    以及本规范安全考虑中§3.7 MEV(恶意提取价值)小节,swcregistry中的SWC-132示例,以及CWE-667中描述的不当锁定问题。
1
2
[S]禁止对连续可变长度参数进行哈希
被测代码不得对连续的可变长度参数使用abi.encodePacked()。

在哈希之前,abi.encodePacked()的每个可变长度参数的元素按顺序打包。通过在连续的可变长度参数之间重新排列元素,同时保持它们连接顺序不变,可能导致哈希冲突。

1
2
3
4
[S]禁用selfdestruct()
被测代码不得包含selfdestruct()指令或其现已弃用的别名suicide(),除非满足以下覆盖要求集:
- [M] 保护自毁操作
- [M] 记录特殊代码用途

如果selfdestruct()指令(或其已弃用的替代项suicide())未得到妥善保护,恶意代码可以调用它并销毁合约,发送合约持有的任何以太币,从而可能窃取资金。还可以与CREATE2结合使用来更改特定地址的代码。此功能可能破坏不变性和去信任保证,引入众多安全问题。此外,一旦合约被销毁,发送的任何以太币都将丢失,这与禁用合约不同,后者会导致发送以太币的交易回滚。

自Solidity编译器版本0.8.18solidity-release-818起,selfdestruct()已被正式弃用,不鼓励使用。

参见[swcregistry]中的SWC-106EIP-6049

1
2
3
4
5
6
7
8
9
[S]禁用assembly{}
被测代码不得包含assembly{}指令,除非满足以下覆盖要求集:
- [M] 避免常见的assembly{}攻击向量
- [M] 记录特殊代码用途
- [M] assembly{}中的编译器错误SOL-2022-5
- [M] 编译器错误SOL-2022-7
- [M] 编译器错误SOL-2022-4
- [M] 编译器错误SOL-2021-3
以及如果使用Solidity编译器版本0.5.5或0.5.6,还需满足[EthTrust-sl-v1]中的[M] assembly{}中的编译器错误SOL-2019-2。

assembly{}指令允许包含更低级别的代码。这使作者能够更强烈地控制生成的字节码,例如可用于优化gas使用。然而,它也潜在地暴露了许多漏洞和错误,这些都是额外的攻击面,并且有多种方法可以使用assembly{}引入故意设计的难以检测的恶意代码。

5.1.1 文本和同形字

1
2
3
4
5
[S]无Unicode方向控制字符  
被测代码不得包含任何Unicode方向控制字符
U+2066、U+2067、U+2068、U+2029、
U+202A、U+202B、U+202C、U+202D或U+202E
除非它满足覆盖要求[M]无不必要的Unicode控制。

使用不可见的Unicode方向控制字符改变字符的显示顺序,可以在查看源代码时掩盖恶意代码,欺骗人工审计员。关于Unicode方向控制字符的更多信息,请参见W3C注释《如何为双向文本使用Unicode控制》unicode-bdo

5.1.2 外部调用

1
2
3
[S] 检查外部调用返回  
使用低级调用函数(即call()、delegatecall()、staticcall()和send())进行外部调用的被测代码必须检查每次使用的返回值以确定调用是否失败,除非它满足覆盖要求
[M] 处理外部调用返回。

另请参见相关要求:[M]保护外部调用,和[Q]验证外部调用。
通常,调用中的异常会导致回滚。这将”冒泡”传播,除非在try/catch中处理。然而,Solidity定义了一组低级调用函数:

  • call()
  • delegatecall()
  • staticcall()
  • send()

使用这些函数进行的调用行为不同。它们在失败时不会回滚,而是返回一个布尔值,指示调用是否成功完成。不显式检查返回值可能导致调用者合约中出现意外行为。依赖这些调用在失败时回滚会导致它们在未成功时出现意外行为。另请参见SWC-104 swcregistryerror-handling中描述的错误处理,未检查的返回值如CWE-252所述,以及相关要求:

  • [S] 使用检查-效果-交互,
  • [M] 处理外部调用返回,和
  • [Q] 验证外部调用。
1
2
3
4
5
6
7
8
9
10
[S] 使用检查-效果-交互(Check-Effects-Interaction)  
进行外部调用的被测代码必须使用`检查-效果-交互模式`来防止重入攻击
除非它满足以下覆盖要求集:
- [M] 保护外部调用,和
- [M] 记录特殊代码使用,
或它满足以下覆盖要求集:
- [Q] 验证外部调用,
- [Q] 记录合约逻辑,
- [Q] 记录系统架构,和
- [Q] 按文档实现。

检查-效果-交互模式是在进行任何状态更改之前验证所有前置条件,然后才在外部交互之前完成所有状态更新,然后才执行外部调用。

以这种方式设计合约可显著减少重入攻击的范围。作为此模式的一部分,除了检查特定合约效果外,还可以测试协议不变量,以进一步确保请求不会产生不安全的结果。另请参见§3.4 外部交互和重入攻击,[c-e-i]中”Solidity安全注意事项”solidity-security对”检查-效果-交互”的解释,”Solidity模式”solidity-patterns中的”检查效果交互”,以及freipi

1
2
3
4
5
6
7
8
9
[S] 无delegatecall()  
被测代码不得包含delegatecall()指令, 除非它满足以下覆盖要求集:
- [M] 保护外部调用,和
- [M] 记录特殊代码使用,
或它满足以下覆盖要求集
- [Q] 验证外部调用,
- [Q] 记录合约逻辑,
- [Q] 记录系统架构,和
- [Q] 按文档实现。

delegatecall()指令使外部合约能够操纵调用它的合约的状态,因为代码以调用者的余额、存储和地址运行。

5.1.3 编译器错误

Solidity编译器的不同版本中存在许多已知的安全错误。本小节中的要求确保被测代码不会触发这些错误。要求的名称包括solidity-bugs-json中首次记录的错误的uid,作为可用于查找有关该错误的更多信息的关键字。[solidity-bugs]描述了用于JSON格式错误列表的约定。本小节中的要求按照受影响的最新Solidity编译器版本排序。

注意
实施推荐的良好实践[GP]使用最新编译器意味着被测代码通过本小节中的所有要求。
一些与编译器相关的错误包含在§5.2.5安全级别[M]编译器错误和覆盖要求作为安全级别[M]要求中,因为它们是本小节中的覆盖要求,或者因为它们是安全级别[S]一组覆盖要求的一部分,该要求已经确保无法触发错误。 一些错误是在已知的`Solidity`编译器版本中引入的,而其他错误则已知或假定存在于所有`Solidity`编译器版本中,直到它们被修复。
1
2
3
[S] 编译器错误SOL-2023-3  
包含Yul代码并使用verbatim指令两次的被测代码,每次周围都有相同的代码,
在使用Solidity编译器版本0.8.5至0.8.22(含)时必须禁用块重复数据删除器。

从Solidity编译器版本0.8.5到修复的0.8.22,块重复数据删除器错误地处理了verbatim项,
意味着有时它会根据周围的代码混淆两个项,而不是正确比较它们。
另请参见2023年11月8日的安全警报

1
2
3
4
[S] 编译器错误SOL-2022-6  
ABI编码包含动态组件的元组(包括结构体、返回值或参数列表)的被测代码,
并且其最后一个元素是基本类型uint或bytes32的calldata静态数组,
不得使用Solidity编译器版本0.5.8至0.8.15(含)。

从Solidity编译器版本0.5.8到修复的0.8.15,使用ABIEncoderV2对元组进行ABI编码,
其最后一个组件是基本类型uint或bytes32的calldata静态数组,
可能导致数据损坏。
另请参见2022年8月8日的安全警报

1
2
3
4
5
[S] 编译器错误SOL-2022-5与.push()  
被测代码
- 从calldata或内存复制字节数组
- 其大小不是32字节的倍数,且有一个空的.push()指令写入结果数组,
不得使用早于0.8.15的Solidity编译器版本。

直到Solidity编译器版本0.8.15,复制长度不是32字节倍数的内存或calldata
可能暴露超出复制的数据,这些数据可以通过
assembly{}观察到。
另请参见2022年6月15日的安全警报和相关要求
[M] assembly{}中的编译器错误SOL-2022-5

1
2
3
4
5
6
[S] 编译器错误SOL-2022-3  
- 对同一函数使用内存和calldata指针,且
- 在继承期间更改函数的数据位置,且
- 在仅知道基合约中原始函数签名的位置进行内部调用

不得使用Solidity编译器版本0.6.9至0.8.12(含)。

Solidity编译器版本从0.6.9到修复的0.8.13有一个错误,错误地允许
内部或公共调用使用仅对外部调用有效的简化,将
内存和calldata视为等效指针。
另请参见2022年5月17日的安全警报

1
2
3
4
5
6
[S] 编译器错误SOL-2022-2  
具有嵌套数组的被测代码
- 将其传递给外部函数,或
- 将其作为输入传递给abi.encode(),或
- 在事件中使用它
不得使用Solidity编译器版本0.6.9至0.8.12(含)。

Solidity编译器版本从0.5.8到修复的0.8.13有一个错误,意味着嵌套数组的
单次编码和解码可以读取超出calldatasize()的数据。
另请参见2022年5月17日的安全警报

1
2
3
4
5
6
[S] 编译器错误SOL-2022-1  
被测代码
对短于32字节的bytesNN类型使用数字字面量,或
对任何bytesNN类型使用字符串字面量,
并将此类字面量作为第一个参数传递给abi.encodeCall(),
不得使用Solidity编译器版本0.8.11或0.8.12。

Solidity定义了一组变量类型,统称为
bytesNN或固定长度变量类型,
指定变量的长度为固定的字节数,遵循模式

  • bytes1
  • bytes2
  • bytes32

Solidity编译器版本0.8.11和0.8.12有一个错误,意味着在某些情况下,
abi.encodeCall()会错误编码字面量参数。
另请参见2022年3月16日的安全警报

1
2
[S] 编译器错误SOL-2021-4  
使用短于32字节的自定义值类型的被测代码不得使用Solidity编译器版本0.8.8。

Solidity编译器版本0.8.8有一个错误,为不需要的自定义类型分配了完整的32字节存储空间。
这可能被滥用来读取任意存储,
如果被测代码包含使用不同Solidity编译器版本编译的代码,也可能导致错误。
另请参见2021年9月29日的安全警报

1
2
3
[S] 编译器错误SOL-2021-2  
使用abi.decode()对内存字节数组进行解码的被测代码
不得在Solidity编译器版本0.4.16至0.8.3(含)中使用ABIEncoderV2。

Solidity编译器版本0.4.16引入了一个错误,在0.8.4中修复,意味着ABIEncoderV2
在读取内存字节数组时错误验证了指针,这可能由于指针计算中的溢出错误而导致
读取超出数组区域的数据。
另请参见2021年4月21日的安全警报

1
2
3
4
5
6
7
[S] 编译器错误SOL-2021-1  
具有两个或多个指令
keccak(mem,length)的被测代码,其中
- mem的值相等,且
- length的值不等,且
- length的值不是32的倍数,
不得在早于0.8.3的Solidity编译器版本中使用优化器。

Solidity编译器版本在0.8.3之前有一个优化器错误,意味着keccak哈希,
针对相同内容但不同长度(不是32字节的倍数)计算,
错误地使用了缓存中的第一个值而不是重新计算。
另请参见2021年3月23日的安全警报

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
[S] 使用现代编译器  
被测代码不得使用早于0.8.0的Solidity编译器版本,
除非它满足EEA EthTrust安全等级规范版本2中的所有以下要求,
作为覆盖要求:

[S] 无溢出/下溢
[S] 编译器错误SOL-2020-11-push
[S] 编译器错误SOL-2020-10
[S] 编译器错误SOL-2020-9
[S] 编译器错误SOL-2020-8
[S] 编译器错误SOL-2020-6
[S] 编译器错误SOL-2020-7
[S] 编译器错误SOL-2020-5
[S] 编译器错误SOL-2020-4

并且
被测代码不得使用早于0.6.0的Solidity编译器版本,
除非它满足EEA EthTrust安全等级规范版本1中的所有以下要求,
作为覆盖要求:

[S] 编译器错误SOL-2020-11-length
[S] 编译器错误SOL-2019-10
[S] 编译器错误SOL-2019-3,6,7,9
[S] 编译器错误SOL-2019-8
[S] 编译器错误SOL-2019-5
[S] 编译器错误SOL-2019-4
[S] 编译器错误SOL-2019-2
[S] 编译器错误SOL-2019-1
[S] 显式存储(包括通过其覆盖要求[M] 如果适用则显式声明存储)
[S] 编译器错误SOL-2018-4
[S] 编译器错误SOL-2018-3
[S] 编译器错误SOL-2018-2
[S] 编译器错误SOL-2018-1
[S] 编译器错误SOL-2017-5
[S] 编译器错误SOL-2017-4
[S] 编译器错误SOL-2017-3
[S] 编译器错误SOL-2017-2
[S] 编译器错误SOL-2017-1
[S] 编译器错误SOL-2016-11
[S] 编译器错误SOL-2016-10
[S] 编译器错误SOL-2016-9
[S] 编译器错误SOL-2016-8
[S] 编译器错误SOL-2016-7
[S] 编译器错误SOL-2016-6
[S] 编译器错误SOL-2016-5
[S] 编译器错误SOL-2016-4
[S] 编译器错误SOL-2016-3

有许多已知的编译器错误影响早于0.6.0的Solidity编译器版本,
但对编译器错误的研究往往集中在影响相对现代Solidity编译器版本的错误上,
因此旧Solidity编译器版本中的任何进一步错误很可能只有在被利用后才会被发现并广为人知。
使用现代Solidity编译器版本是一个良好的实践。
在极少数情况下无法使用晚于0.6.0的Solidity编译器版本时,
可以通过符合本规范版本1中定义的相关覆盖要求来实现EEA EthTrust认证。
另请参见相关要求[M] 使用现代编译器
涵盖需要安全等级[M]审查的Solidity编译器错误。

1
2
[S] 无古老编译器  
被测代码不得使用早于0.3的Solidity编译器版本。

未跟踪早于0.3的Solidity编译器版本的编译器错误。
因此存在未知错误可能导致意外问题的风险。
另请参见solidity-bugs-json中的”SOL-2016-1”。

5.2 安全等级[M]

获得安全等级[M]的EEA EthTrust认证意味着合约经过人类审计或团队的手动分析,并且重要的安全问题均已解决至审计者满意为止。这一级安全要求是对等级[S]的补充,通常用于:

  • 合约使用了不常见或高风险特性;
  • 出现静态分析难以确认的安全性;
  • 审计者需通过逻辑判断确定安全性。
1
为满足安全等级[M],测试代码必须首先满足§5.1的所有等级[S]要求,除非补充满足相应的"Overriding Requirement"来替代部分等级[S]要求
1
2
[M]显式消除求值顺序歧义
被测代码​​不得​​包含因变量求值顺序不同而导致结果差异的语句。

Solidity 中函数的求值顺序并非完全确定,且不同编译器版本间无法保证一致性。若语句调用的多个函数对共享状态对象产生副作用,求值顺序的差异可能导致不同结果。
此外,事件日志及addmod/modmul指令的求值顺序通常不符合常规模式,使用这些功能的被测代码可能产生预期外的结果。

警告
​​示例13:不确定的求值顺序​​ 当 g() 和 h() 修改了 f() 依赖的状态变量时,该调用无法保证可复现的结果。 f(g(x), h(y)); // 无法保证结果一致性
✅ 解决方案 通过临时变量显式控制执行顺序:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
​示例 14:使用临时变量强制求值顺序​​
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

uint256 public myNumber;
uint256 public yourNumber;

function firstTransform(uint256 someNumber) public returns (uint256) {
myNumber += 1; // 副作用:修改状态
return someNumber * myNumber; // 依赖更新后的 myNumber
}

function secondTransform(uint256 someNumber) public returns (uint256) {
yourNumber += 3; // 副作用:修改状态
return someNumber / yourNumber;
}

function deterministicResult(uint256 someNumber) public returns (uint256) {
uint256 firstResult = firstTransform(someNumber); // 显式优先执行
return secondTransform(firstResult); // 显式后续执行
}

​​关键点​​: 使用firstResult临时变量分离执行步骤, 确保firstTransformsecondTransform前完成消除多函数调用的状态依赖歧义.
关联参考:

1
2
3
[M]验证精确余额检查
被测代码若检查账户余额​​严格等于​​(==)指定值或变量时,​​必须​​防范转账操作对余额检查的影响。
此为 [S] 级要求禁用精确余额检查的覆盖要求

若智能合约在执行过程中检查账户余额是否等于​​特定精确值​​,则存在潜在风险:攻击者可通过向该账户转账来改变其余额,导致交易意外回滚等非预期结果。若必须使用此类检查,​​必须​​实施防护措施以抵御此类攻击可能性。

5.2.1 文本与同形字攻击

本节要求与安全公告CVE-2021-42574CWE-94“代码生成控制不当”(亦称”代码注入”)相关。

1
2
3
[M]禁用非必要Unicode控制符
被测代码​​不得​​使用Unicode方向控制字符,除非对文本正确渲染确有必要, 并且渲染结果不会误导读者
(此为 [S]级要求禁用Unicode方向控制字符的覆盖要求)

​​安全等级[M]允许​​在文本字符串中使用Unicode方向控制字符,但需经必要性分析。

1
2
[M] 禁止同形字式攻击
被测代码​​不得​​使用同形字、Unicode控制符、组合字符或多Unicode区块字符,若其效果具有误导性。

通过替换不同字母表中视觉相似的字符(如拉丁字母 “a” 与西里尔字母 “а”)、使用方向控制符或组合字符,可构造恶意代码欺骗审计人员。例如:
使用 “í” 冒充 “i” 或 “ì”
阿拉伯语 “ت” 冒充 “ث”
数字 “1” 冒充字母 “l”
数学符号 “𝚒” 冒充拉丁字母 “i”
此类攻击称为​​同形字攻击​​,具体手法详见Ivanov
若变量名/标签中​​确需混用多Unicode区块字符​​(如双语混合命名),只要不产生误导或混淆,仍可通过EEA EthTrust认证。
审计人员判定存在​​非必要误导或混淆​​时,视为不符合要求。
​​关联要求​​:[S]禁用Unicode方向控制字符

5.2.2 外部调用防护

1
2
3
4
5
6
7
8
9
10
11
12
13
[M] 必须保护所有外部调用
[M] Protect External Calls
如果测试代码中有外部调用(external call),则必须确保:
- 被调用地址对应的是同一 "Tested Code" 集合中的确切合约代码;
- 所有被调用合约都属于 Tested Code;
- 被调用合约由同一实体控制;
- 对于这些调用,必须提供与 Checks‑Effects‑Interactions 模式等效的重入攻击防御机制。
- 否则必须满足以下 等级 [Q] 级别的 Overriding 条件:
- [Q] Verify External Calls
- [Q] Document Contract Logic
- [Q] Document System Architecture
- [Q] Implement as Documented
这些额外条件允许部分外部调用通过场景理解和文档审计替代自动模式检测

安全级别[M]的EEA EthTrust认证允许在构成测试代码一部分的一组合约内进行调用。这可确保在此安全级别一起审核所有调用的合约。

如果合同调用了未作为测试代码的一部分进行审计的已知外部合约,则可以通过覆盖要求来证明符合此要求,这允许验证者根据自己的判断声称所调用的合同提供了适当的安全性。在这种情况下,通过实施覆盖性要求来声明符合性时适用的围绕测试代码文档的扩展要求反映了如果审查者简单地假设外部合同是安全的,因为它们已被广泛使用,则可能会带来非常高的风险。

除非测试代码自己部署合约,并准确检索其地址以进行调用,否则有必要检查合约是否真的部署在测试代码中假设的地址。

必须为测试代码提供与安全级别[S]使用[c-e-i]相同级别的针对重入攻击的保护级别。如果使用重入防护的合约不遵循 [c-e-i],它们仍然容易受到攻击。跨函数重入攻击已经成功,其中共享相同状态的单独函数在外部调用后导致状态更改。

1
2
[M]防御只读重入攻击
被测试代码必须可以预防只读重入攻击

如§3.4外部交互和重入攻击中所述,从函数读取信息的代码最终可能会读取不一致或不正确的信息。当测试代码调用出现这种可能性的函数时,调用代码需要适当的机制来避免这种情况发生。
测试代码如果调用其他合约的view函数,必须确保调用不会读取到中间不一致状态。这通常需要使用锁机制或状态检查modifier,防止读取过程中被外部重入造成不一致。

警告
示例15:不安全方法:回放一个可以重入view函数的值
这是一个简单的只读重入攻击案例。
合约 Reentered 有一个视图函数,该函数根据特定 LPToken 中的 totalSupply 和 numberOfEther 确定价格,以及一个公共非重入函数,该函数出售 LP 代币并将收到的 ETH 发送给调用它的用户。
当攻击者合约中调用攻击函数时,它会减少重新输入合约中 numberOfEther 的值,并在更新 totalSupply 之前触发攻击者合约的 receive() 函数,方法是阻止指令 LPToken.burnFrom(msg.sender,amount)执行。
攻击者合约的 receive()函数调用了被攻击的 Reentered 合约的 buyToken()函数。由于 numberOfEther 的值已被修改,但 totalSupply 没有修改,因此被攻击合约中的 buyToken() 函数将请求错误数量的代币作为交换。比例是攻击会收到更多的代币。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

contract Reentered {
function getPrice() public returns (uint256) {
return totalSupply / numberOfEther; // 计算价格
}
function sellLPToken(uint256 amount) public nonReentrant {
// ...
numberOfEther -= amountToReceive;
(bool success, ) = msg.sender.call{value: amountToReceive}("");
require(success, "Transfer failed");
LPToken.burnFrom(msg.sender, amount);
}
}

contract Attacked {
function buyToken(uint256 amount) public {
uint256 tokenToReceive = Reentered.getPrice() * amount;
// ...
}
}

contract Attacker {
function attack() public {
Reentered.sellLPToken(LPToken.balanceOf(address(this)));
// 触发 ETH 转账,从而触发 receive()
}
receive() external payable {
Attacked.buyToken(1000);
}
}
在上述场景中,`receive()`中的`getPrice()`调用读取了还未更新的状态,导致计算错误,是典型的`view`函数重入攻击示例。

5.2.3 有防御性的代码使用说明

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[M]记录特殊代码使用并说明防护措施
所有包含下面内容的实例都必须在合约文档中说明其使用理由,并阐明相应的安全防护或设计依据,且文档须对合约调用者可见:
- CREATE2
- assembly { … }
- selfdestruct() 或 suicide()
- 外部调用
- delegatecall()
- 有可能发生溢出/下溢的操作
- block.number 或 block.timestamp
- 使用预言机或伪随机性

这是对以下多个 [S] 需求的 Overriding Requirement:
- [S] No CREATE2
- [S] No selfdestruct()
- [S] No assembly {}
- [S] Use Check-Effects-Interaction
- [S] No delegatecall()

所有这些编码模式都有合法用途,但它们也是安全漏洞的潜在原因。因此,安全级别 [M] 需要测试这些模式的使用是否得到解释和合理性,并且它们的使用方式不会引入已知漏洞。

记录外部调用使用的要求适用于测试代码中的所有外部调用,无论它们是否满足相关要求[S]使用Check-Effects-Interaction

另请参阅相关要求:[Q] 文档契约逻辑、[Q] 文档系统架构、[Q] 按文档实现,[Q] 验证外部调用,[M] 避免常见汇编 {} 攻击向量,[M] 汇编 {} 中的编译器错误 SOL-2022-5,[M] 编译器错误 SOL-2022-4,[M] 编译器错误 SOL-2021-3,如果使用 Solidity 编译器版本 0.5.5 或 0.5.6,[M] [EthTrust-sl-v1] 中汇编 {} 中的编译器错误 SOL-2019-2。

1
2
3
4
5
[M]确保数值计算的舍入不会被滥用
若合约存在依赖舍入操作的数学逻辑,比如四舍五入、整除等:
- 必须文档化可能产生的误差范围;
- 不可因舍入造成价值意外增加或丢失;
- 舍入逻辑设计应防止攻击者通过来回交换谓"round‑trip"不断获利(如累计额外份额)。

智能合约通常使用整数算术实现实数上的数学公式。此类代码可能会引入舍入误差,因为大小有界的整数和有理数无法精确表示同一范围内的所有实数。
如果使用舍入的过程导致可预测的错误量,从而增加往返产生的值,则可以通过重复该过程来累积虹吸大笔金额来利用该差异。

警告
示例Example 16: 舍入不安全写法
四舍五入到最接近的可用数字的简单交换可能意味着往返实际上会产生一个 off-by-one 错误,因此来回交换正确数量的代币将在每笔交易中产生比开始时更多的价值
1
2
3
4
5
6
7
8
9
10
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

function xChangeTo(uint256 numberOfEth) public returns (uint256) {
return numberOfEth.mul(rateThatCausesRounding); // 四舍五入
}

function xChangeFrom(uint256 numberOfOtherToken) public returns (uint256) {
return numberOfOtherToken.div(rateThatCausesRounding); // 四舍五入
}
为了防止此漏洞,“保留更改(Keep in Change)”方法确保产生的任何差异都不会为重复调用智能合约的攻击者提供优势。重要的是要注意,差异仍然会产生。合约可以使用“过度服务”,反复调用受“保留零钱”方法保护的交换,从用户那里窃取。
1
2
3
4
5
6
7
8
9
10
// SPDX-License-Identifier: MIT
pragma solidity 0.8.18;

function xChangeTo(uint256 numberOfEth) public returns (uint256) {
return numberOfEth.mulDown(rateThatCausesRounding); // 向下舍入
}

function xChangeFrom(uint256 numberOfOtherToken) public returns (uint256) {
return numberOfOtherToken.divDown(rateThatCausesRounding); // 向下舍入
}
该方法确保每次交易不会给攻击者带来优势,尽管仍可能累积舍入损失. 这个漏洞在实践中已经在DeFi协议智能合约中被发现,可能会使数亿美元面临风险。DevCon 2023 演讲[DevCon 四舍五入](https://archive.devcon.org/resources/6/tackling-rounding-errors-with-precision-analysis.pdf)的演示幻灯片中提供了进一步的解释。[舍入误差](https://entethalliance.org/specs/ethtrust-sl/v3/#bib-rounding-errors)中提供了自动做市商整数舍入的全面数学分析示例。

此要求基于 CWE-1339精度不足或实数准确度不足。

1
2
3
4
5
6
[M]保护合约自毁操作
如合约中使用 selfdestruct() 或其旧别名 suicide():
- 必须保证只有授权方才能调用;
- 必须采用与该操作与作者声明一致的保护逻辑。

除非补充具备 [Q] Enforce Least Privilege 合规文档说明,否则不允许直接使用该操作。该项是对 "[S] No selfdestruct()" 的 Overriding Requirement

如果selfdestruct()指令(或其已弃用的替代suicide())没有得到仔细保护,恶意代码可以调用它并破坏合约,并可能窃取合约持有的任何以太币。此外,这可能会扰乱合约的其他用户,因为一旦合约被销毁,发送的任何以太币都会丢失,这与合约被禁用时不同,这会导致发送以太币的交易恢复。
SWC-106

示例(Example 18): 这是 Parity 钱包多重签失败导致以太资金被冻结的案例,因合约中selfdestruct()被错误保护触发所引致。
1
2
3
4
5
[M] 避免常见assembly{} 攻击向量
测试的代码不得使用汇编assembly{} 指令来更改变量,除非代码不能:
- 创建存储指针冲突,也不会
- 允许将任意值分配给函数类型的变量。
这是 [S] No assembly {} 的一组覆盖要求的一部分。

assembly{}指令为开发人员提供了一种在智能合约中生成代码的低级方法。使用这种方法提供了极大的灵活性和控制力,例如降低gas成本。但是,它也暴露了一些可能的攻击面,恶意编码人员可能会在其中引入难以检测的攻击。此要求可确保不会公开两个众所周知的此类攻击面。

另请参阅 SWC-124 和 SWC-127 [swcregistry],以及相关要求 [M] 文档特殊代码使用、[M] 编译器错误 SOL-2022-7、[M] 编译器错误 SOL-2022-5 在程序集中 {},[M] 编译器错误 SOL-2022-4、[M] 编译器错误 SOL-2021-3,如果使用 Solidity 编译器版本 0.5.5 或 0.5.6,则 [M] [EthTrust-sl-v1] 中的程序集 {} 中的编译器错误 SOL-2019-2。

1
2
3
4
5
6
7
8
9
10
11
[M]保护使用 CREATE2 的调用
任何使用 CREATE2 部署合约时,需满足:
- 被部署合约必须包含在 Tested Code 内;
- 不得使用 selfdestruct(), delegatecall() 或 callcode();
- 必须与合约作者的声明逻辑一致。
否则必须满足
- [Q] Verify External Calls,
- [Q] Document Contract Logic,
- [Q] Document System Architecture,
- [Q] Implement as Documented
这四项 Overriding Requirement

CREATE2能够与链上代码尚不存在的地址进行交互,因此防止外部调用尚未知的恶意或不安全的合约代码非常重要。

测试代码需要包含可以使用CREATE2部署的任何代码,以验证保护是否到位以及代码的行为是否符合协定作者声明。这包括确保不存在可以改变不变性或转发调用的作码,例如使用CREATE2部署的合约,例如selfdestruct()delegatecall()callcode()

如果存在这些操作码中的任何一个,则需要覆盖要求所需的额外保护和文档。

1
2
3
4
[M] 安全溢流/下溢
测试的代码不得包含可以溢出或下溢的计算,除非
- 有明显的需求(例如,用于模运算)和
- 如有必要,任何计算都有保护措施,以确保行为与合同作者的主张一致。

在少数情况下,算术溢出或下溢是预期行为。对此类案件进行适当保护非常重要。
请注意,Solidity 编译器版本0.8.0引入了导致事务恢复的溢出保护。
SWC-101

1
2
[M]伪随机性来源
测试代码中使用的随机性来源必须具有足够的抵抗力,无法预测其目的得到满足。

这一要求涉及对每个具体合同和案例的仔细评估。随机性的某些用途依赖于没有比任何其他预测更准确的预测。对于这种情况,可以准确猜测或由矿工或验证者控制的值,例如区块难度、时间戳和/或区块编号,会引入漏洞。因此,需要像预言机服务这样的“强”随机性源。

其他用途对“好的猜测”有抵抗力,因为使用接近但错误的东西不会比任何其他猜测提供更大的获得优势的可能性。

警告
示例 19:不要这样做:随机性容易受到近似猜测的影响
一种在特定时间猜测链的区块数的竞赛,奖励最接近正确答案的答案,使用的是“随机性”来源,该源很容易受到近似猜测的影响。
示例 20:抗近似的随机性
只有当提交的号码与未来将要举行的链下彩票中的中奖条目完全匹配时才会支付彩票,在能够近似答案方面没有优势。
另见[S] No Exact Balance Check, [M] Don't Misuse Block Data, and [Q] Protect against MEV Attacks.
1
2
[M] 不要滥用块数据
测试代码中使用的块号和时间戳不得给 MEV 或类似攻击带来漏洞。

区块数很容易受到近似预测的影响,尽管它们通常不是可靠、精确的经过时间指标。block.timestamp会受到恶意行为者的操纵。因此,重要的是,测试代码不信任这些数据,因为它们是高度可靠或随机的信息。

swcregistry 中对 SWC-116 的描述包括一些要避免的技术的代码示例,例如使用block.number / 14 作为经过的秒数的代理,或依靠 block.timestamp 来指示精确的时间已经过去。

对于概率低精度的使用,例如“大约 1/2 小时已经过去了”,像 (block.number / 14 > 1800) 这样的表达式在主网上或具有类似规则区块周期约为 14 秒的区块链上可以足够稳健。但是使用这种方法来确定例如“恰好 36 秒”已经过去了,则无法满足要求。

警告 如果依赖特定区块周期的合约部署在区块频率截然不同的区块链上,可能会带来严重的风险。
同样,由于 `block.timestamp` 依赖于可被恶意节点运营商操纵的设置,因此在以太坊主网等情况下,它适合用作粗粒度近似值(以分钟为单位),但不同区块链上的相同代码可能容易受到 MEV 攻击。

请注意,这与预言机的使用有关,预言机也可能提供不准确的信息。

另请参阅相关要求 [S] 无精确余额检查、[M] 随机性来源和 [Q] 防止 MEV 攻击。

5.2.4 签名管理

1
2
[M] 正确的签名验证
测试代码必须正确验证签名,以确保链下签名的消息的真实性。

一些智能合约处理链下签名的消息,以提高灵活性,同时保持真实性。执行自己的签名验证的智能合约需要验证此类消息的真实性。

使用 ecrecover() 进行签名验证,根据预期结果验证返回的地址非常重要。特别是,返回值address(0) 表示未能提供有效签名。

另见 SWC-122 swcregistry

对于使用 ecrecover()和早于 0.4.14 的Solidity编译器版本的代码,请参阅相关要求 [M] 使用现代编译器,特别是 [M] 验证 [EthTrust-sl-v1] 中的 ecrecover() 输入

1
2
3
4
5
6
7
[M] 不当使用签名进行重放攻击保护
使用签名来防止重放攻击的测试代码必须确保签名不能重复使用:
- 在同一函数中验证相同的消息,也不会
- 在多个函数中验证测试代码中的相同消息,也
- 在多个合约地址中验证同一消息,其中同一帐户可能正在签署消息,也
- 在跨多个链的同一个合约地址中,
除非它满足优先要求 [Q] 预期重播。此外,测试代码必须验证不能为同一消息创建多个签名,就像可延展签名的情况一样。

在重放攻击中,攻击者重放正确签名的消息以利用系统。签名消息需要包含足够的标识信息,以便明确定义其预期设置。

可延展签名允许攻击者为同一邮件创建新签名。如果使用可延展的签名,检查签名哈希值以确保消息仅被处理过一次的智能合约可能容易受到重放攻击。

5.2.5 安全级别 [M] 编译器错误和覆盖要求

§5.1.3编译器错误中描述的一些solidity编译器错误在安全级别[M]上具有覆盖要求,并且有些具有在软件中不易检测到的触发条件。

注意
实施推荐的良好实践 [GP] 使用最新编译器意味着测试代码通过了本小节中的所有要求。
1
2
[M] Solidity 编译器错误 2023-1
包含使用 .selector 的具有副作用的复合表达式的测试代码必须将 viaIR 选项与 0.6.2 到 0.8.20 之间的 Solidity 编译器版本一起使用。
Solidity 编译器版本 0.6.2 中引入并在 Solidity 编译器版本 0.8.21 中修复的一个bug, 这个bug是当复合表达式访问`.selector`成员时,除非使用 viaIR 管道,否则不会计算表达式。因此,不会发生由该表达引起的任何副作用。

另请参阅 2023 年 7 月 19 日的安全警报

1
2
3
[M] 编译器错误 SOL-2022-7
​​经测试的代码中,若存在存储写入操作后跟随条件性提前终止(该终止由包含return()或stop()指令的内联汇编函数触发),则绝对禁止使用 Solidity 编译器版本 0.8.13 至 0.8.16(含)进行编译。​
这是 [S] No assembly {} 的覆盖要求集的一部分。

Solidity 编译器版本 0.8.17 中修复的一个错误意味着在优化期间,存储写入后从内联汇编函数有条件提前终止有时会被错误地丢弃。

另请参阅 2022 年 9 月 5 日的安全警报

1
2
[M] Compiler Bug SOL‑2022‑5 in assembly {}
当从 calldata 或 memory 中拷贝字节数据长度不是 32 字节倍数,并用 assembly {} 读取时,Solidity 0.8.14 及以下(不含 0.8.15)版本存在超界可见漏洞,必须禁止使用老的版本。

在 Solidity 编译器版本 0.8.15 之前,复制长度不是 32 字节倍数的内存或调用数据可能会暴露超出复制数据的数据,这可以使用assmebly {} 观察到。

另请参阅 2022 年 6 月 15 日安全警报相关要求 [S] 编译器错误 SOL-2022-5 与 .push() 、[M] 避免常见assmebly {} 攻击向量、[M] 文档特殊代码使用、[M] 编译器错误 SOL-2022-4 和 [M] 编译器错误 SOL-2021-3。

1
2
[M] Compiler Bug SOL‑2022‑4
在不同的 assembly {} 片段中写入与读取时使用共享内存,但某个优化路径下丢失该内存,Solidity 0.8.13 或 0.8.14 不可使用yulOptimizer

Solidity 编译器版本 0.8.13 引入了一个yulOptimizer错误,该错误在 Solidity 编译器版本 0.8.15 中修复,其中在assembly {} 指令中创建但仅在不同的assembly {} 指令中读取的内存被丢弃。

另请参阅 2022 年 6 月 17 日安全警报和相关要求 [M] 避免常见assmebly {} 攻击向量、[M] 文档特殊代码使用、[M] 编译器 Bug SOL-2022-7、[M] 程序集 {} 中的编译器 Bug SOL-2022-5 和 [M] 编译器 Bug SOL-2021-3。

1
2
[M] Compiler Bug SOL‑2021‑3
在 assembly {} 中读取 immutable 但短于 256 位的带符号整数时,Solidity 0.6.5 到 0.8.8(含) 存在错误,不可使用

Solidity 编译器版本 0.6.8 引入了一个错误,该错误在 Solidity 编译器版本 0.8.9 中修复,这意味着在内联assembly {} 指令中可能会错误地读取短于 256 位的不可变有符号整数类型。

另请参阅 2021 年 9 月 29 日安全警报和相关要求 [M] 安全使用程序集 {}、[M] 文档特殊代码使用、[M] 程序集 {} 中的编译器错误 SOL-2022-5 和 [M] 编译器错误 SOL-2022-4。

1
2
3
4
5
6
7
8
9
[M] Use a Modern Compiler
测试的代码不得使用早于 0.8.0 的 Solidity 编译器版本,除非它满足 EEA EthTrust 安全级别规范第 2 版中的 [M] 编译器错误检查构造函数付款的要求,作为优先要求, 并且
测试代码不得使用早于 0.6.0 的 Solidity 编译器版本,除非它满足 EEA EthTrust 安全级别规范第 1 版中的以下所有要求,作为覆盖要求:
- [M] 编译器错误 SOL-2020-2,
- [M] 汇编中的编译器错误 SOL-2019-2 {},
- [M] 编译器错误检查标识调用,
- [M] 验证 ecrecover() 输入,
- [M] 编译器错误-无零以太发送,以及
- [M] 显式声明存储。

5.3 安全等级[Q]

除了可自动的静态测试验证(安全级别 [S])和手动审核(安全级别 [M])之外,安全级别 [Q] 的 EEA EthTrust 认证还意味着检查测试代码的预期功能是否得到充分记录,可以验证其功能正确性,代码和文档是否经过人工审核员或审核团队的彻底审查,以确保它们内部连贯且相互一致, 足够仔细地识别复杂的安全漏洞。

这种级别的审查对于使用 ERC20 ERC20、ERC721 ERC721 等的代币尤其相关;token-standards 标识了可以定义标记的许多其他标准。

在此安全级别,还需要检查以确保代码不包含不直接影响安全性但会影响代码质量的错误。代码经常被复制,因此安全级别 [Q] 要求代码尽可能编写好。要解决的风险是,在复制现有代码作为起点后引入弱点很容易,而且并不少见。

1
2
[Q] 通过安全级别 [M]
要获得安全级别 [Q] 的 EEA EthTrust 认证,经过测试的代码必须满足 § 5.2 安全级别 [M] 的要求。
1
2
[Q] 将 TimeLock 延迟用于敏感操作
影响所有或大多数用户的敏感作必须使用 [TimeLock] 延迟。

敏感操作,例如智能合约升级和 RBAC 更改,会影响协议中的所有或大多数用户。TimeLock 延迟允许用户在不同意提议的更改时退出系统,并允许开发人员在检测到可疑更改时做出反应。

1
2
3
4
5
6
7
8
9
10
11
[Q] 代码 Linting
测试的代码

- 不得创建不必要的变量,并且
- 不得对可能在同一范围内出现的函数、变量或其他标记使用相同的名称,并且
- 不得包含在正常作中失败的 assert() 语句,并且
- 不得包含执行中无法访问的代码
- 明确用于管理意外错误的代码除外,例如 assert() 语句,以及
- 不得包含与智能合约同名的函数,除非使用 constructor 关键字将其显式声明为构造函数,并且
- 必须显式声明所有函数和变量的可见性,并且
- 必须在其编译指示中指定一个或多个 Solidity 编译器版本。

代码通常从“好示例”复制,作为开发的起点。达到安全级别 [Q] EEA EthTrust 认证的代码意味着高质量,因此确保复制它不会鼓励坏习惯非常重要。查看不包含无意义代码的测试代码也更容易。

函数和变量具有相同名称的代码通常更难阅读。如果这些项目在范围上可以重叠,编译将消除它们的歧义,但它们通常需要审稿人进行大量工作才能在精神上分离。在多个不重叠的循环中使用 i,j 作为计数器是可以的,但是在“外部作用域”中有一个变量,其名称被其他一些变量或函数复制在“内部作用域”中会增加遵循执行模式所需的工作量。

明确允许​​设计用于捕获意外错误的代码, 例如 assert()指令,因为若防御性编写的代码成功消除了触发特定错误的可能性,却无法获得 EEA EthTrust 认证,将非常遗憾。assert()语句​​仅适用于不变量的检查​​,而非作为通用错误处理机制。若在常规操作中因将assert()作为错误捕获机制而导致失败,​​应替换为require()语句​​或专为该用例设计的类似机制。若因编码缺陷导致失败,则​​必须修复该缺陷​​。

assert()语句的要求基于 CWE-670 始终不正确的控制流实现。

1
2
[Q] 管理Gas使用量增加
必须有足够的 Gas 来处理测试代码中随时间增长的数据结构,根据 [Q] 文档契约逻辑提供的描述。

一些结构(例如数组)可以增长,并且变量的值(根据设计)是可变的。迭代一个事先不清楚大小的结构,无论是增长的数组、变化的边界还是由外部值决定的东西,都可能导致 gas 使用量显着增加。需要在预期的业务逻辑的上下文中考虑什么是合理的增长,以及测试代码如何防止 Gas Griefing 攻击,在这些攻击中,恶意参与者或错误导致值超出预期的合理范围。

另请参阅 SWC-126SWC-128 [swcregistry] 和第 5.3.1 节文档要求中的相关要求。

1
2
[Q] 保护Gas使用
测试的代码必须防止恶意行为者窃取或浪费gas。

允许“无 Gas”交易的智能合约使用户无需提供自己的 Gas 即可提交交易。需要仔细实施它们,以防止 Gas GriefingGas Siphoning 攻击的拒绝服务。

另请参阅 The Gas Siphon Attack: How it Happened and How to Protect Yourself from the DevCon 2019 talk DevCon-siphoning

1
2
[Q] 防止 Oracle 预言机故障
经过测试的代码必须保护自己免受其所依赖的 Oracle 中的故障。

众所周知,一些预言机很容易受到操纵,例如,因为它们提供的信息来自易受只读重入攻击的信息,或者通过使用闪电贷来纵价格以启用 MEV 攻击,以及其他众所周知的攻击。
此外,作为网络软件,预言机可能会遇到从延迟问题到彻底失败或停产等问题。
重要的是要检查预言机用于生成其提供的信息的机制,以及依赖该预言机的测试代码是否可能受到其失败的影响,或者恶意行为者纵其输入或代码以启用攻击的影响。
另请参阅相关要求 [Q] 防范排序攻击和 [Q] 防范 MEV 攻击。

1
2
[Q] 防止排序攻击
测试的代码必须以防止排序攻击的方式管理信息。

在排序攻击中,攻击者将其交易置于与受害者交易相比的有利位置。这可以由恶意区块生产者或监控内存池的攻击者完成,并通过广播他们自己的交易并以更高的交易费用抢占易受攻击的交易。取消激励措施是通常缓解措施,通过应用哈希承诺方案 hash-commit 或批量执行等缓解措施。
另请参阅相关要求 [Q] 防范 MEV 攻击。

1
2
[Q] 防范 MEV 攻击
易受 MEV 攻击的测试代码必须遵循适当的设计模式来降低这种风险。

MEV是指区块生产者可以恶意重新排序或压制交易,或者区块链中的另一个参与者可以提议交易或采取其他行动来获得他们本不应该获得的利益的可能性。
这一要求需要审计师仔细判断测试代码如何容易受到MEV攻击,以及哪些缓解策略是合适的。一些方法在§ 3.7 MEV(恶意提取值)中进一步讨论。
需要考虑许多攻击类型,包括排序攻击。
另请参阅相关要求 [S] 无精确余额检查、[M] 随机性来源、[M] 不滥用区块数据、[Q] 防止 Oracle 故障和 [Q] 防止排序攻击。

1
2
[Q] 防止治理接管
包含治理系统的测试代码必须防止对治理设计的恶意利用。

恶意利用很难准确定义,因为它取决于系统的既定目标 - 例如 [Q] 文档契约逻辑中描述的目标。从广义上讲,此要求是检查测试代码是否能够抵御恶意实体为获得治理控制权而做出的努力,这些控制权预计将归属于指定的受信任实体,或者分散和广泛分布,以提供治理决策和所采取的行动得到广泛支持的安全感。
治理攻击特定于被利用的系统。根据治理提案系统的不同,某些漏洞领域可能包括:

  • 发行的治理代币;
  • 治理代币的分配方法;
  • 治理提案的接受和执行的设计。
    例如,如果质押合约用于分配治理代币作为奖励,那么质押合约不易受到闪电贷攻击的攻击非常重要,在闪电贷攻击中,大量代币在非常短期的闪电贷中借入,并以原子方式质押以获得临时大多数治理代币,然后用于做出治理决策, 例如耗尽被攻击者钱包中持有的所有资金。

另请参阅相关要求 [Q] 防止排序攻击。

1
2
[Q] 处理所有输入
测试的代码必须验证输入,并且无论输入符合设计还是格式错误,都能正常运行。

未能验证输入的代码可能会被恶意制作的输入,从而触发错误或作者没有预料到的行为。
另请参见 SWC-123 swcregistry,其中指出重要的是要考虑输入要求是否过于严格,以及是否过于宽松,CWE-573 调用者不正确遵循规范,并注意 §5.1.3 编译器错误中有一些特定于特定Solidity 编译器版本的相关要求。

1
2
[Q] 状态更改触发事件
测试的代码必须为导致状态更改的所有事务发出合约事件。

事件是方便的接口,可在 EVM 的日志记录功能之上提供抽象。应用程序可以通过以太坊客户端的 RPC 接口订阅和监听这些事件。更多信息请访问 solidity-events

事件通常被期望用于记录所有状态变化,因为它们不仅对链下应用程序有用,而且对安全监控和调试也很有用。记录合约中的所有状态更改可确保与合约交互的任何开发人员都能了解作为ABI一部分的每个状态更改,并可以通过事件注释了解预期行为,如 Q 使用NatSpec注释代码。

1
2
[Q] 没有私人数据
测试代码不得将私有数据存储在区块链上。

这是安全级别 [Q] 要求,主要是因为什么是私人数据的问题通常需要仔细和深思熟虑的评估以及对上下文的合理理解。一般来说,这可能包括对如何收集数据的评估,以及数据提供者被告知哪些信息的使用情况。

本规范中的私有数据用于指代不打算向公众普遍提供的信息。例如,个人的家庭电话号码通常是私人数据,而企业的客户查询电话号码通常不是私人数据。同样,识别个人帐户的信息通常是私人数据,但在某些情况下它是公共数据。在这种情况下,可以按照此要求将公共数据记录在链上。

警告 请注意:在某些情况下,[GDPR] 等法规对某些私人数据施加了正式的法律要求。但是,针对此要求执行测试会导致专家技术意见,说明审计师认为私有的数据是否被泄露。关于 Tested Code 是否满足此要求的声明不代表任何形式的法律建议或意见、律师代理等。
1
2
[Q] 预期重播
如果测试代码中的签名可以重复使用,则重放实例必须是有意的、记录的并且可以安全地重复使用。

这是 [M] 不得不当使用重放攻击保护签名的首要要求。

在极少数情况下,测试代码的意图可能是允许重放签名。例如,签名可以用作在给定时间段内参与白名单的权限。在这些特殊情况下,重播必须作为已知限额包含在文档中。此外,必须验证重用不会被恶意利用。

5.3.1 文档要求

安全级别 [Q] 一致性需要详细描述测试代码的行为方式。除了详细的测试要求以检查它是否在特定已知漏洞方面确实按照描述的行为外,重要的是对其提出的声明是准确的。此要求有助于确保测试代码满足审计特定文档之外的声明。

这些要求的结合有助于确保测试代码中没有隐藏恶意代码,例如恶意“后门”或“定时炸弹”。由于存在充当“定时炸弹”、“电话回家”等行为的代码的合法用例,因此这种组合有助于确保测试专注于实际问题。

本节中的要求扩展了满足安全级别 [M] 要求 [M] 文档特殊代码使用所需的覆盖范围。与该要求一样,此级别有多个要求需要本小节中规定的文件。

1
2
[Q] 合同逻辑文档
测试代码功能旨在实现的业务逻辑规范必须可供任何可以调用测试代码的人使用。

以人类可读的格式记录的契约逻辑,并具有足够的细节,审计员可以验证特殊代码使用的功能正确性和安全性假设,帮助他们更高效、更有信心地评估复杂的代码。

重要的是要记录逻辑如何防范潜在的攻击,例如闪电贷攻击(尤其是治理或价格操纵)、MEV 和其他利用生态系统功能或代币经济学的复杂攻击。

1
2
[Q] 系统架构文档
必须提供测试代码的系统架构文档,以传达总体系统设计、特权角色、安全假设和预期用途。

系统文档提供审计员信息,以了解安全假设并确保功能正确性。如果系统文档包含在代码存储库的自述文件中或引用,以及有关如何测试、构建和部署源代码的文档,这将很有帮助。

随着时间的推移,变量和更复杂的数据结构的管理是本文档的重要组成部分。这一要求的这一方面可能会通过满足相关要求 [Q] 管理gas使用增加。另请参阅相关要求 [Q] 使用 NatSpec 注释代码。

1
2
[Q] 威胁模型文档
必须提供测试代码的记录威胁模型,描述每个威胁、安全假设、预期响应和预期结果。

威胁模型是一种工具,可帮助为可能单独或协同引发的各种攻击做好准备,提供一组假设但可能的场景,以确保测试代码能够充分抵御这些攻击。

一个好的威胁模型将涵盖一系列可能性,包括

  • 出现以前未知的攻击媒介
  • 网络操纵
  • 经济战
  • 治理开发
  • 预言机攻击
    以及其他场景以及此类场景的组合,这些场景可能会压倒测试代码对它们的防御。
1
2
[Q] 使用 NatSpec 注释代码
测试代码中包含的所有公共接口必须根据 [NatSpec] 格式使用内联注释进行批注,这些注释解释每个函数、参数、事件和返回变量背后的意图,以及安全使用的开发人员说明。

内联注释对于确保开发人员和审计员了解每个函数和其他代码组件背后的意图非常重要。公共接口是指编译的测试代码的ABI中包含的任何内容。还建议对实现敏感和/或复杂逻辑的私有或内部函数使用内联注释。

遵循 [NatSpec] 格式允许 Solidity 编译器理解这些内联注释,以便将它们提取为机器可读格式,其他第三方工具可用于安全评估和自动文档,包括与 Sourcify 等源代码验证工具集成的钱包向用户显示的文档。这也可用于生成完全或部分满足 [Q] 合约逻辑文档。

1
2
[Q] 按文档实施
测试代码的行为必须按照 [Q] 合约逻辑文档和 [Q] 系统架构文档提供的文档中所述进行。

安全级别 [Q] 提供文档的要求很重要。然而,测试代码的实际行为与记录的一样也很重要。如果没有,则可能反映出不够谨慎,并且由于实施中遗漏的错误,代码也容易受到攻击。也有可能,差异是试图在测试代码中隐藏恶意代码。

5.3.2 访问控制

1
2
3
[Q] 强制执行最低权限
启用特权访问的经过测试的代码必须根据为 [Q] 合约逻辑文档,实现适当的访问控制机制,为这些交互提供所需的最低权限。
这是 [M] 保护自毁的覆盖性要求。

有几种常见的方法来实现访问控制,例如基于角色的访问控制 RBACOwnable,并且通常针对给定用例实施定制访问控制。使用行业标准方法可以帮助简化审计过程,但不足以确定不存在因实施错误或恶意制作的合同而产生的风险。

在协议运维和部署级别考虑访问控制非常重要。如果一个协议是以确定性的方式部署的,例如允许多链部署在所有链上具有相同的地址,那么显式设置所有者而不是默认为msg.sender 很重要,因为这可能会留下一个简单的工厂部署契约作为协议的不足的新管理员。

正如 SWC-105 中所述,适当的访问控制适用于支付尤为重要,但其他作(例如 SWC-124 中描述的数据覆盖或更改特定的访问控制)也需要得到适当的保护 [swcregistry]。此要求与 CWE-284 不正确的访问控制匹配。

另请参阅 solidity-patterns 中的“访问限制”。

1
2
[Q] 使用可撤销和可转让的访问控制权限
如果测试代码将访问控制用于特权操作,则它必须实现撤销和转移这些权限的机制。

特权帐户可以对合同集执行管理任务。如果这些帐户被泄露或执行这些任务的责任分配给不同的人,那么拥有撤销和转移这些权限的机制非常重要。

1
2
[Q] 没有特权操作的单个管理员 EOA
如果测试代码将访问控制用于特权操作,则它必须确保所有关键管理任务都需要执行多个签名,除非存在具有更高权限的管理员(multisg),并且可以在 EOA 受到损害或恶意操作的情况下撤销权限并撤销权限。

特权帐户可以对合同集执行管理任务。如果单个 EOA 可以执行这些操作,并且该权限无法撤销,那么私钥泄露或丢失对智能合约构成的风险可能是存在的。

1
2
3
4
5
[Q] 验证外部调用
包含外部调用的测试代码必须
- 记录对它们的需求,并且
- 以与合约作者的主张完全兼容的方式保护它们。
这是 [S] 使用 check-effects-interaction 和 [m] 保护外部调用的一组覆盖性要求的一部分。

在安全级别 [Q] 审计师可以非常灵活地为外部调用的不同用途提供 EEA EthTrust 认证。
此要求实际上允许审阅者声明外部调用的目的地不存在安全风险。值得注意的是,任何此类声明都非常密切地反映了审稿人的声誉。
仅仅因为智能合约被广泛使用就假设它是安全的是不合适的,假设用户提供的智能合约未来会是安全的也是不可接受的——这是一个已知的载体,已被用于许多严重的安全漏洞。
同样重要的是要考虑审阅者引用和声明安全的任何代码如何容易受到基于其使用外部调用的攻击。
举一个常见的例子,如果其中一个合约是恶意的,或者只是易受攻击,则允许用户提供任何一对代币合约的交换合约可能会面临风险,而交换合约无法预料和防范。
另请参阅相关要求 [Q] 文档契约逻辑、[Q] 文档系统架构和 [Q] 文档方式实施。

1
2
3
4
5
[Q] 验证 tx.origin 使用情况
对于使用 tx.origin 的测试代码,每个实例
- 必须与测试代码中规定的安全性和功能目标一致,并且
- 不得允许另一个合约违反针对 [Q] 文档合约逻辑或 [Q] 文档系统架构所做的关于合约功能的断言,即使该合约是由授权直接与测试代码交互的用户调用的。
这是 [S] No tx.origin 的优先要求。

tx.origin可用于启用网络钓鱼攻击,诱骗用户与合约进行交互,从而访问其账户中的所有资金。对于调用方的授权,msg.sender 是更安全的选择。

另请参阅相关要求 [Q] 文档合约逻辑、[Q] 强制执行最低权限、Solidity 安全注意事项 [solidity-security] 中的“tx.origin”部分和 CWE 284:不正确的访问控制 CWE-284

1
2
[Q] 指定 Solidity 编译器版本以产生一致的输出
测试代码必须在其编译指示中指定一系列 Solidity 版本,这些版本在给定相同编译选项的情况下生成相同的字节码。

不同的编译器版本可能存在不同的安全漏洞并引入意外行为。虽然编译器升级几乎总是会改善安全特性,但无法保证将来所做的任何给定更改都是这种情况。

因此,如果通过遵守测试代码生成的字节码发生变化,则该规范需要进行新的评估,以获得安全级别 [Q] 的 EEA EthTrust 认证。

显式指定确切的 Solidity 版本可以防止与其他版本进行编译,从而确保开发、审计和部署之间的一致性。指定一系列 Solidity 编译器版本,以便使用相同的设置使用该范围内的任何 Soldity 编译器版本编译测试代码会产生相同的字节码,这意味着只要已知编译器没有更改需要进行新的分析,EEA Ethtrust 测试代码认证就有效。

注意
此要求的措辞允许在编译指示中指定开放式范围,例如
1
pragma solidity >=0.8.14;
但是,EEA EthTrust 认证需要指定其有效的特定范围的 Solidity 编译器版本,以防 Solidity 编译器的未来版本编译测试代码以生成不同的字节码。 这是为了确保在编译器的升级不会导致生成的字节码发生任何变化的情况下,为较新的 Solidity 编译器版本重新认证测试代码几乎不需要工作。
请注意,即使使用特定的编译指示,声称是相同版本、优化设置和其他构建配置参数的不同编译器实现也会影响生成的字节码。

5.4 推荐的良好实践

本节介绍了需要大量人工判断来评估的良好实践,其中没有明确的方法来确定是否已完成,或者糟糕的实施可能会降低而不是提高测试代码的安全性。测试和满足这些要求不会直接影响对本文档的一致性。但是请注意,满足推荐的良好实践 [GP] 满足尽可能多的要求在实践中意味着测试代码满足基于编译器错误的所有要求,包括大多数安全级别 [S] 要求。

1
2
[GP]检查并解决新的安全漏洞
检查 [solidity-bugs-json] 和其他来源,了解 2023 年 11 月 1 日之后宣布的错误并解决它们。

该版本的规范于 2023 年底最终确定。新的漏洞会不时被发现,而且时间表是不可预测的。此版本中考虑的最新 solidity 编译器错误是 SOL-2023-3。

检查发布太晚而无法合并到本文档当前版本中的安全警报是维护尽可能高安全性的重要技术。

还有其他有关新安全漏洞的信息来源,从 CWE 到关注许多面向安全的组织的博客,例如那些为该规范做出贡献的组织。

1
2
[GP]满足尽可能多的要求
测试代码应在高于其认证的安全级别的安全级别上满足本规范的尽可能多的要求。

虽然满足更高 EEA EthTrust 认证安全级别的一些要求不会改变测试代码的正式一致性级别,但每个要求都是指定的,因为满足这些要求可以防止特定的已知攻击。如果可以满足特定要求,即使不需要在被测试的安全级别上保持一致性,满足该要求将提高测试代码的安全性,因此值得这样做。

1
2
[GP]使用最新编译器
测试代码应该使用最新的可用稳定 Solidity 编译器版本。

Solidity 编译器会定期更新以提高性能,但也专门用于修复发现的安全漏洞。§ 5.1.3 编译器错误中有许多要求,这些要求与编写本规范时已知的漏洞有关,以及为默认提供更好的安全性而进行的增强功能。一般来说,较新的 Solidity 编译器版本会提高安全性。除非有特定的已知原因不这样做,否则使用可用的最新 Solidity 编译器版本将带来更好的安全性。

1
2
[GP]编写清晰易读的 Solidity 代码
测试代码应该编写以便于理解。

没有严格的规则来定义如何编写清晰的代码。使用足够的描述性名称、适当注释代码并使用易于理解的结构而不会导致代码变得过大非常重要,因为这也使其难以阅读和理解。

过多的嵌套、非结构化注释、复杂的循环结构以及对变量和函数使用非常简洁的名称都是编码样式的示例,这些样式也会使代码更难理解。

重要的是要注意,在某些情况下,开发人员可以牺牲易读性来换取其他好处,例如降低 gas 成本——这可以通过有据可查的代码在一定程度上缓解。

同样,对于涉及多个单独智能合约的复杂代码,将源代码组织到多个文件中的方式可以帮助澄清正在发生的事情。特别是,命名源代码文件以匹配它们定义的智能合约的名称是一种常见的模式,可以简化理解。

此良好实践在某种程度上扩展了相关要求 [Q] 代码 Linting,但关于如何满足它的判断必然比要求所确立的细节更加主观。那些寻求有关代码样式的额外指导的人可以参考 Solidity-Style-Guide

1
2
[GP]遵循公认的 ERC 标准
当测试代码有合理能力为其用例遵守最终的 [ERC] 标准时,它应该符合最终的 [ERC] 标准。

ERCEIP(以太坊改进提案)的一类,它定义了应用程序级标准和约定,包括代币标准 ERC20 和名称注册表 ERC137等智能合约标准。

虽然遵循 ERC 标准本质上不会使 Solidity 代码安全,但它们确实使开发人员能够与通用接口集成并遵循预期行为的已知约定。如果测试代码确实声称遵循给定的 ERC,则审计师可以验证其在符合该标准方面的功能正确性。

1
2
[GP]定义软件许可证
测试代码应定义软件许可证

软件许可证为贡献者和用户(包括审计员和白帽)如何与代码交互提供法律指导。由于部署到公共网络的字节码可以被任何人读取,因此通常的做法是对用于生成它的 Solidity 代码使用开源许可证。

选择最能满足项目需求的 软件许可证 非常重要,并在整个测试代码和文档中清楚地链接到它,例如,在代码存储库中使用一个突出的 LICENSE 文件并从每个源文件中引用它。

1
2
[GP]负责任地披露新漏洞
本规范未解决的安全漏洞应通过 § 1.4 反馈和新漏洞中所述的负责任披露提请工作组和其他人注意。

不时发现新的安全漏洞。它有助于修订此规范的工作,以确保工作组了解新的漏洞或有关现有已知漏洞的新知识。

EEA已同意管理此类通知的特定电子邮件地址 - 如果发生变化,则相应地更新此规范。

1
2
[GP]使用模糊测试
模糊测试应用于探测测试代码是否存在错误。

有效的模糊测试可能需要数天甚至数周的时间:耐心等待总比过早停止要好。

由于模糊测试依赖于语料库,因此维护该语料库以最大限度地提高代码覆盖率非常重要,并且有助于修剪不必要或重复的输入以提高效率。

示例 21:使用 `Scribble` 的模糊测试规范
使用 `Scribble` 编写的属性的简单示例,`Scribble` 是一种规范语言,可将 `Solidity` 中的注释转换为具体断言。注释是“///”之后的注释。从正在检查的注解派生的属性确保如果对合约 `Foo` 和函数 `add` 的调用成功,则状态变量 x 必须等于调用 - `old(x)` - 添加到函数参数 y 之前的值。从本质上讲,使用此属性进行模糊测试将检查状态的存储是否正确更新。
1
2
3
4
5
6
7
8
contract Foo {
int x;
/// #if_succeeds {:msg "test"} x == old(x) + y;
function add(int y) public {
require(y > 0);
x += y;
}
}
模糊测试规则和属性可能很复杂,并且取决于特定的合约、函数、变量、它们在执行前和/或之后的值,以及可能的许多其他事情。如果 `Fuzzing` 在 `Solidity` 编译器版本中发现任何漏洞,请负责任地披露。
1
2
[GP]使用突变测试
突变测试应该用于评估和改进智能合约测试套件的质量。
突变测试是一种基于故障的测试技术,它将人为缺陷(突变)引入源代码,以发现测试覆盖率中的潜在差距。

有几类突变算子与智能合约特别相关:
状态变量突变: 修改状态变量声明和操作,包括:

  • 更改可见性修饰符(公共/私有)
  • 更改常量值
  • 修改存储位置
    算术突变: 更改算术运算以测试数值计算:
  • 替换运算符 ( , -, *, /)
  • 修改边界条件
  • 引入溢出/下溢条件
    控制流突变: 通过合约更改执行路径:
  • 反转条件语句
  • 修改循环条件
  • 更改函数修饰符
    访问控制突变: 安全关键型权限检查:
  • 删除所有权检查
  • 更改基于角色的权限
  • 修改身份验证逻辑
    将突变测试集成到 CI/CD 管道中会很有帮助,并具有适当的性能基准(例如,最小突变分数阈值、测试超时限制)和退出标准(例如,关键路径突变覆盖率、所需的突改运算符)。
1
2
[GP]使用形式验证
测试代码应该经过正式验证。

形式验证是一系列技术,可以从数学上证明智能合约的功能正确性。它已用于其他应用,例如嵌入式系统。形式验证在智能合约中有很多用途,例如测试活跃性、高级别的协议不变量以确保安全性,或者证明程序执行的更窄、更具体的属性。

在形式验证中,创建智能合约预期或期望结果的正式(符号或数学)规范,从而能够对协议的正确性进行正式的数学证明。为此,智能合约本身通常被翻译成正式语言。

存在多种语言和程序来创建原始验证证明,其中一些明确的目的是使临时用户和非数学家更容易访问形式验证。请参阅 EF-SL 了解一些示例。

当由具有经验和技能的从业者正确实施时,形式化验证可以保证模糊测试和测试无法提供的保证。然而,这在实践中往往很难实现。形式验证需要大量的体力劳动和专业知识。

与单元或集成测试、模糊测试或其他方法相比,全面的形式化验证很可能具有更高的成本和复杂性。许多智能合约的不可变性,以及在可能的情况下升级合约的复杂性,使得形式验证对协议的管理员和利益相关者具有吸引力。

1
2
[GP]为多重签名钱包选择合适的阈值
特权操作的多重签名要求应该有足够数量的签名者,并且不需要“1 of N”或所有签名。

管理操作要求多个签名已成为许多团队的标准。如果管理不当,即使智能合约代码是安全的,它们也可能成为攻击源。

“1 of N”设置的问题在于,它相对容易被利用。同时,“N of N”设置意味着,即使有一个签名者无法访问其帐户或不批准某个操作,也不可能获得批准。这可能会影响必要的操作,例如用另一个签名者替换一个签名者,例如确保作连续性,这可能会产生非常严重的影响。

选择较少数量的签名来满足要求可以更快地做出响应,而较高的值则需要更强的多数支持。考虑使用“M of N”多重签名,其中 M = (N/2) 1,换句话说,批准所需的尽可能小的大多数签名作为起点。但是,重要的是要考虑有多少潜在签名者以及需要签名的具体情况,以确定给定情况下 M 的合理值。

A. Additional Information 额外信息

A.1 Defined Terms

The following is a list of terms defined in this Specification.

Access Control Mutations
Arithmetic Mutations
Back-Running
Black-Box Fuzzing
Censorship Attacks
Checks-Effects-Interactions
Control Flow Mutations
Corpus
Economic Warfare
EEA EthTrust Certification
EVM
EVM versions
Execution Contract
Fixed-length Variable
Flash Loan Attack
Formal Verification
Front-Running
Fuzzing
Gas Griefing
Gas Siphoning
Gas Tokens
Governance Exploitation
Gray-Box Fuzzing
Hard Fork
Hash Collisions
Homoglyph Attacks
Infrastructure Security
Invariant Testing
Invariants
Like This
Logic Contract
Low-level Call Functions
Malleable Signatures
Metamorphic Upgrade
Metamorphic Upgrades
MEV
Monitoring and Response
Mutation Score
Mutation Testing
Network Manipulation
Network Upgrade
Operational Security Measures
Oracle Attacks
Oracles
Ordering Attacks
Overriding Requirements
Private Data
Privileged Accounts
Property-Based Testing
Proxy Contract
Public Interfaces
Re-entrancy Attacks
Read-only Re-entrancy Attack
Related Requirements
Sandwich Attacks
Security Level [M]
Security Level [Q]
Security Level [S]
Security Levels
Set Of Contracts
Set of Overriding Requirements
State Variable Mutations
Static Analysis
Symbolic Execution
Test Coverage
Test-driven Development
Tested Code
Testnet
Threat Model
TWAP
Unicode Direction Control Characters
Unit Testing
Upgradable Contract
Valid Conformance Claim
White-Box Fuzzing

A.2 Summary of Requirements

This section provides a summary of all requirements and Recommended Good Practices in this Specification.

[S] Encode Hashes with chainid
Tested code MUST create hashes for transactions that incorporate chainid values following the recommendation described in [EIP-155]

[S] No CREATE2
Tested code MUST NOT contain a CREATE2 instruction.
unless it meets the Set of Overriding Requirements

[M] Protect CREATE2 Calls, and
[M] Document Special Code Use,
[S] No tx.origin
Tested code MUST NOT contain a tx.origin instruction
unless it meets the Overriding Requirement [Q] Verify tx.origin Usage

[S] No Exact Balance Check
Tested code MUST NOT test that the balance of an account is exactly equal to (i.e. ==) a specified amount or the value of a variable
unless it meets the Overriding Requirement [M] Verify Exact Balance Checks.

[S] No Hashing Consecutive Variable Length Arguments
Tested Code MUST NOT use abi.encodePacked() with consecutive variable length arguments.

[S] No selfdestruct()
Tested code MUST NOT contain the selfdestruct() instruction or its now-deprecated alias suicide()
unless it meets the Set of Overriding Requirements

[M] Protect Self-destruction, and
[M] Document Special Code Use.
[S] No assembly {}
Tested Code MUST NOT contain the assembly {} instruction
unless it meets the Set of Overriding Requirements

[M] Avoid Common assembly {} Attack Vectors,
[M] Document Special Code Use,
[M] Compiler Bug SOL-2022-5 in assembly {},
[M] Compiler Bug SOL-2022-7,
[M] Compiler Bug SOL-2022-4,
[M] Compiler Bug SOL-2021-3, and
if using Solidity compiler version 0.5.5 or 0.5.6, [M] Compiler Bug SOL-2019-2 in assembly {} in [EthTrust-sl-v1].
[S] No Unicode Direction Control Characters
Tested code MUST NOT contain any of the Unicode Direction Control Characters U+2066, U+2067, U+2068, U+2029, U+202A, U+202B, U+202C, U+202D, or U+202E
unless it meets the Overriding Requirement [M] No Unnecessary Unicode Controls.

[S] Check External Calls Return
Tested Code that makes external calls using the Low-level Call Functions (i.e. call(), delegatecall(), staticcall(), and send()) MUST check the returned value from each usage to determine whether the call failed,
unless it meets the Overriding Requirement [M] Handle External Call Returns.

[S] Use Check-Effects-Interaction
Tested code that makes external calls MUST use the Checks-Effects-Interactions pattern to protect against Re-entrancy Attacks
unless it meets the Set of Overriding Requirements

[M] Protect external calls, and
[M] Document Special Code Use,
or it meets the Set of Overriding Requirements

[Q] Verify External Calls,
[Q] Document Contract Logic,
[Q] Document System Architecture, and
[Q] Implement as Documented.
[S] No delegatecall()
Tested Code MUST NOT contain the delegatecall() instruction
unless it meets the Set of Overriding Requirements:

[M] Protect External Calls, and
[M] Document Special Code Use,

or it meets the Set of Overriding Requirements
[Q] Verify External Calls,
[Q] Document Contract Logic,
[Q] Document System Architecture, and
[Q] Implement as Documented.
[S] Compiler Bug SOL-2023-3
Tested code that includes Yul code and uses the verbatim instruction twice, in each case surrounded by identical code, MUST disable the Block Deduplicator when using a Solidity compiler version between 0.8.5 and 0.8.22 (inclusive).

[S] Compiler Bug SOL-2022-6
Tested code that ABI-encodes a tuple (including a struct, return value, or a parameter list) that includes a dynamic component with the ABIEncoderV2, and whose last element is a calldata static array of base type uint or bytes32, MUST NOT use a Solidity compiler version between 0.5.8 and 0.8.15 (inclusive).

[S] Compiler Bug SOL-2022-5 with .push()
Tested code that

copies bytes arrays from calldata or memory whose size is not a multiple of 32 bytes, and
has an empty .push() instruction that writes to the resulting array,
MUST NOT use a Solidity compiler version older than 0.8.15.

[S] Compiler Bug SOL-2022-3
Tested code that

uses memory and calldata pointers for the same function, and
changes the data location of a function during inheritance, and
performs an internal call at a location that is only aware of the original function signature from the base contract
MUST NOT use a Solidity compiler version between 0.6.9 and 0.8.12 (inclusive).

[S] Compiler Bug SOL-2022-2
Tested code with a nested array that

passes it to an external function, or
passes it as input to abi.encode(), or
uses it in an event
MUST NOT use a Solidity compiler version between 0.6.9 and 0.8.12 (inclusive).

[S] Compiler Bug SOL-2022-1
Tested code that

uses Number literals for a bytesNN type shorter than 32 bytes, or
uses String literals for any bytesNN type,
and passes such literals to abi.encodeCall() as the first parameter, MUST NOT use Solidity compiler version 0.8.11 nor 0.8.12.

[S] Compiler Bug SOL-2021-4
Tested Code that uses custom value types shorter than 32 bytes MUST NOT use Solidity compiler version 0.8.8.

[S] Compiler Bug SOL-2021-2
Tested code that uses abi.decode() on byte arrays as memory MUST NOT use the ABIEncoderV2 with a Solidity compiler version between 0.4.16 and 0.8.3 (inclusive).

[S] Compiler Bug SOL-2021-1
Tested code that has 2 or more occurrences of an instruction keccak(mem,length) where

the values of mem are equal, and
the values of length are unequal, and
the values of length are not multiples of 32,
MUST NOT use the Optimizer with a Solidity compiler version older than 0.8.3.

[S] Use a Modern Compiler
Tested code MUST NOT use a Solidity compiler version older than 0.8.0, unless it meets all the following requirements from the EEA EthTrust Security Levels Specification Version 2, as Overriding Requirements:

[S] No Overflow/Underflow
[S] Compiler Bug SOL-2020-11-push
[S] Compiler Bug SOL-2020-10
[S] Compiler Bug SOL-2020-9
[S] Compiler Bug SOL-2020-8
[S] Compiler Bug SOL-2020-6
[S] Compiler Bug SOL-2020-7
[S] Compiler Bug SOL-2020-5
[S] Compiler Bug SOL-2020-4
AND

Tested code MUST NOT use a Solidity compiler version older than 0.6.0, unless it meets all the following requirements from the EEA EthTrust Security Levels Specification Version 1, as Overriding Requirements:

[S] Compiler Bug SOL-2020-11-length
[S] Compiler Bug SOL-2019-10
[S] Compiler Bugs SOL-2019-3,6,7,9
[S] Compiler Bug SOL-2019-8
[S] Compiler Bug SOL-2019-5
[S] Compiler Bug SOL-2019-4
[S] Compiler Bug SOL-2019-2
[S] Compiler Bug SOL-2019-1
[S] Explicit Storage (including through its overriding requirement [M] Declare storage Explicitly if appropriate)
[S] Compiler Bug SOL-2018-4
[S] Compiler Bug SOL-2018-3
[S] Compiler Bug SOL-2018-2
[S] Compiler Bug SOL-2018-1
[S] Compiler Bug SOL-2017-5
[S] Compiler Bug SOL-2017-4
[S] Compiler Bug SOL-2017-3
[S] Compiler Bug SOL-2017-2
[S] Compiler Bug SOL-2017-1
[S] Compiler Bug SOL-2016-11
[S] Compiler Bug SOL-2016-10
[S] Compiler Bug SOL-2016-9
[S] Compiler Bug SOL-2016-8
[S] Compiler Bug SOL-2016-7
[S] Compiler Bug SOL-2016-6
[S] Compiler Bug SOL-2016-5
[S] Compiler Bug SOL-2016-4
[S] Compiler Bug SOL-2016-3
[S] No Ancient Compilers
Tested code MUST NOT use a Solidity compiler version older than 0.3.

[M] Pass Security Level [S]
To be eligible for EEA EthTrust certification at Security Level [M], Tested code MUST meet the requirements for § 5.1 Security Level [S].

[M] Explicitly Disambiguate Evaluation Order
Tested code MUST NOT contain statements where variable evaluation order can result in different outcomes

[M] Verify Exact Balance Checks
Tested code that checks whether the balance of an account is exactly equal to (i.e. ==) a specified amount or the value of a variable. MUST protect itself against transfers affecting the balance tested.
This is an Overriding Requirement for [S] No Exact Balance Check.

[M] No Unnecessary Unicode Controls
Tested code MUST NOT use Unicode direction control characters unless they are necessary to render text appropriately, and the resulting text does not mislead readers.
This is an Overriding Requirement for [S] No Unicode Direction Control Characters.

[M] No Homoglyph-style Attack
Tested code MUST not use homoglyphs, Unicode control characters, combining characters, or characters from multiple Unicode blocks, if the impact is misleading.

[M] Protect External Calls
For Tested code that makes external calls:

all addresses called by the Tested code MUST correspond to the exact code of the Set of Contracts tested, and
all contracts called MUST be within the Tested code, and
all contracts called MUST be controlled by the same entity, and
the protection against Re-entrancy Attacks for the Tested Code MUST be equivalent to what the Checks-Effects-Interactions pattern offers,
unless it meets the Set of Overriding Requirements

[Q] Verify External Calls,
[Q] Document Contract Logic,
[Q] Document System Architecture, and
[Q] Implement as Documented.
This is an Overriding Requirement for [S] Use Check-Effects-Interaction.

[M] Avoid Read-only Re-entrancy Attacks
Tested Code that makes external calls MUST protect itself against Read-only Re-entrancy Attacks.

[M] Handle External Call Returns
Tested Code that makes external calls MUST reasonably handle possible errors.
This is an Overriding Requirement for [S] Check External Calls Return.

[M] Document Special Code Use
Tested Code MUST document the need for each instance of:

CREATE2,
assembly {},
selfdestruct() or its deprecated alias suicide(),
external calls,
delegatecall(),
code that can cause an overflow or underflow,
block.number or block.timestamp, or
Use of oracles and pseudo-randomness,
and MUST describe how the Tested Code protects against misuse or errors in these cases, and the documentation MUST be available to anyone who can call the Tested Code.

This is part of several Sets of Overriding Requirements, one for each of

[S] No CREATE2,
[S] No selfdestruct(),
[S] No assembly {},
[S] Use Check-Effects-Interaction, and
[S] No delegatecall().
[M] Ensure Proper Rounding of Computations Affecting Value
Tested code MUST identify and protect against exploiting rounding errors:

The possible range of error introduced by such rounding MUST be documented.
Tested code MUST NOT unintentionally create or lose value through rounding.
Tested code MUST apply rounding in a way that does not allow round-trips “creating” value to repeat causing unexpectedly large transfers.
[M] Protect Self-destruction
Tested code that contains the selfdestruct() or suicide() instructions MUST

ensure that only authorised parties can call the method, and
MUST protect those calls in a way that is fully compatible with the claims of the contract author,
unless it meets the Overriding Requirement [Q] Enforce Least Privilege.

This is an Overriding Requirement for [S] No selfdestruct().

[M] Avoid Common assembly {} Attack Vectors
Tested Code MUST NOT use the assembly {} instruction to change a variable unless the code cannot:

create storage pointer collisions, nor
allow arbitrary values to be assigned to variables of type function.
This is part of a Set of Overriding Requirements for [S] No assembly {}.

[M] Protect CREATE2 Calls
For Tested Code that uses the CREATE2 instruction, any contract to be deployed using CREATE2

MUST be within the Tested Code, and
MUST NOT use any selfdestruct(), delegatecall() nor callcode() instructions, and
MUST be fully compatible with the claims of the contract author,
unless it meets the Set of Overriding Requirements

[Q] Verify External Calls,
[Q] Document Contract Logic,
[Q] Document System Architecture, and
[Q] Implement as Documented.
This is part of a Set of Overriding Requirements for [S] No CREATE2.

[M] Safe Overflow/Underflow
Tested code MUST NOT contain calculations that can overflow or underflow unless

there is a demonstrated need (e.g. for use in a modulo operation) and
there are guards around any calculations, if necessary, to ensure behavior consistent with the claims of the contract author.
[M] Sources of Randomness
Sources of randomness used in Tested Code MUST be sufficiently resistant to prediction that their purpose is met.

[M] Don’t Misuse Block Data
Block numbers and timestamps used in Tested Code MUST NOT introduce vulnerabilities to MEV or similar attacks.

[M] Proper Signature Verification
Tested Code MUST properly verify signatures to ensure authenticity of messages that were signed off-chain.

[M] No Improper Usage of Signatures for Replay Attack Protection
Tested Code using signatures to prevent replay attacks MUST ensure that signatures cannot be reused:

In the same function to verify the same message, nor
In more than one function to verify the same message within the Tested Code, nor
In more than one contract address to verify the same message, in which the same account(s) may be signing messages, nor
In the same contract address across multiple chains,
unless it meets the Overriding Requirement [Q] Intended Replay. Additionally, Tested Code MUST verify that multiple signatures cannot be created for the same message, as is the case with Malleable Signatures.

[M] Solidity Compiler Bug 2023-1
Tested code that contains a compound expression with side effects that uses .selector MUST use the viaIR option with Solidity compiler versions between 0.6.2 and 0.8.20 inclusive.

[M] Compiler Bug SOL-2022-7
Tested code that has storage writes followed by conditional early terminations from inline assembly functions containing return() or stop() instructions MUST NOT use a Solidity compiler version between 0.8.13 and 0.8.16 inclusive.
This is part of the Set of Overriding Requirements for [S] No assembly {}.

[M] Compiler Bug SOL-2022-5 in assembly {}
Tested code that

copies bytes arrays from calldata or memory whose size is not a multiple of 32 bytes, and
has an assembly {} instruction that reads that data without explicitly matching the length that was copied,
MUST NOT use a Solidity compiler version older than 0.8.15.
This is part of the Set of Overriding Requirements for [S] No assembly {}.

[M] Compiler Bug SOL-2022-4
Tested code that has at least two assembly {} instructions, such that

one writes to memory e.g. by storing a value in a variable, but does not access that memory again, and
code in a another assembly {} instruction refers to that memory,
MUST NOT use the yulOptimizer with Solidity compiler versions 0.8.13 or 0.8.14.
This is part of the Set of Overriding Requirements for [S] No assembly {}.

[M] Compiler Bug SOL-2021-3
Tested code that reads an immutable signed integer of a type shorter than 256 bits within an assembly {} instruction MUST NOT use a Solidity compiler version between 0.6.5 and 0.8.8 (inclusive).
This is part of the Set of Overriding Requirements for [S] No assembly {}.

[M] Use a Modern Compiler
Tested code MUST NOT use a Solidity compiler version older than 0.8.0, unless it meets the requirement [M] Compiler Bug Check Constructor Payment from the EEA EthTrust Security Levels Specification Version 2, as an Overriding Requirement,

AND

Tested code MUST NOT use a Solidity compiler version older than 0.6.0, unless it meets all the following requirements from the EEA EthTrust Security Levels Specification Version 1, as Overriding Requirements:

[M] Compiler Bug SOL-2020-2,
[M] Compiler Bug SOL-2019-2 in assembly {},
[M] Compiler Bug Check Identity Calls,
[M] Validate ecrecover() input,
[M] Compiler Bug No Zero Ether Send, and
[M] Declare storage Explicitly.
[Q] Pass Security Level [M]
To be eligible for EEA EthTrust Certification at Security Level [Q], Tested code MUST meet the requirements for § 5.2 Security Level [M].

[Q] Use TimeLock Delays for Sensitive Operations
Sensitive operations that affect all or a majority of users MUST use [TimeLock] delays.

[Q] Code Linting
Tested code

MUST NOT create unnecessary variables, and
MUST NOT use the same name for functions, variables or other tokens that can occur within the same scope, and
MUST NOT include assert() statements that fail in normal operation, and
MUST NOT include code that cannot be reached in execution
except for code explicitly intended to manage unexpected errors, such as assert() statements, and
MUST NOT contain a function that has the same name as the smart contract unless it is explicitly declared as a constructor using the constructor keyword, and
MUST explicitly declare the visibility of all functions and variables, and
MUST specify one or more Solidity compiler versions in its pragma directive.
[Q] Manage Gas Use Increases
Sufficient Gas MUST be available to work with data structures in the Tested Code that grow over time, in accordance with descriptions provided for [Q] Document Contract Logic.

[Q] Protect Gas Usage
Tested Code MUST protect against malicious actors stealing or wasting gas.

[Q] Protect against Oracle Failure
Tested Code MUST protect itself against malfunctions in Oracles it relies on.

[Q] Protect against Ordering Attacks
Tested Code MUST manage information in such a way that it protects against Ordering Attacks.

[Q] Protect against MEV Attacks
Tested Code that is susceptible to MEV attacks MUST follow appropriate design patterns to mitigate this risk.

[Q] Protect Against Governance Takeovers
Tested Code which includes a governance system MUST protect against malicious exploitation of the governance design.

[Q] Process All Inputs
Tested Code MUST validate inputs, and function correctly whether the input is as designed or malformed.

[Q] State Changes Trigger Events
Tested code MUST emit a contract event for all transactions that cause state changes.

[Q] No Private Data
Tested code MUST NOT store Private Data on the blockchain.

[Q] Intended Replay
If a signature within the Tested Code can be reused, the replay instance MUST be intended, documented, and safe for re-use.

This is an Overriding Requirement for [M] No Improper Usage of Signatures for Replay Attack Protection.

[Q] Document Contract Logic
A specification of the business logic that the Tested code functionality is intended to implement MUST be available to anyone who can call the Tested Code.

[Q] Document System Architecture
Documentation of the system architecture for the Tested code MUST be provided that conveys the overrall system design, privileged roles, security assumptions and intended usage.

[Q] Document Threat Models
Documented Threat Models for the Tested code MUST be provided, describing each threat, security assumptions, expected responses, and expected outcomes.

[Q] Annotate Code with NatSpec
All Public Interfaces contained in the Tested code MUST be annotated with inline comments according to the [NatSpec] format that explain the intent behind each function, parameter, event, and return variable, along with developer notes for safe usage.

[Q] Implement as Documented
The Tested code MUST behave as described in the documentation provided for [Q] Document Contract Logic, and [Q] Document System Architecture.

[Q] Enforce Least Privilege
Tested code that enables privileged access MUST implement appropriate access control mechanisms that provide the least privilege necessary for those interactions, based on the documentation provided for [Q] Document Contract Logic.
This is an Overriding Requirement for [M] Protect Self-destruction.

[Q] Use Revocable and Transferable Access Control Permissions
If the Tested code makes uses of Access Control for privileged actions, it MUST implement a mechanism to revoke and transfer those permissions.

[Q] No Single Admin EOA for Privileged Actions
If the Tested code makes uses of Access Control for privileged actions, it MUST ensure that all critical administrative tasks require multiple signatures to be executed, unless there is a multisg admin that has greater privileges and can revoke permissions in case of a compromised or rogue EOA and reverse any adverse action the EOA has taken.

[Q] Verify External Calls
Tested Code that contains external calls

MUST document the need for them, and
MUST protect them in a way that is fully compatible with the claims of the contract author.
This is part of a Set of Overriding Requirements for [S] Use Check-Effects-Interaction, and for [M] Protect External Calls.

[Q] Verify tx.origin Usage
For Tested Code that uses tx.origin, each instance

MUST be consistent with the stated security and functionality objectives of the Tested Code, and
MUST NOT allow assertions about contract functionality made for [Q] Document Contract Logic or [Q] Document System Architecture to be violated by another contract, even where that contract is called by a user authorized to interact directly with the Tested Code.
This is an Overriding Requirement for [S] No tx.origin.

[Q] Specify Solidity Compiler Versions to Produce Consistent Output
The Tested Code MUST specify a range of Solidity versions in its pragma directive(s) that produce the same Bytecode given the same compilation options.

[GP] Check For and Address New Security Bugs
Check [solidity-bugs-json] and other sources for bugs announced after 1 November 2023 and address them.

[GP] Meet as Many Requirements as Possible
The Tested Code SHOULD meet as many requirements of this specification as possible at Security Levels above the Security Level for which it is certified.

[GP] Use Latest Compiler
The Tested Code SHOULD use the latest available stable Solidity compiler version.

[GP] Write Clear, Legible Solidity Code
The Tested Code SHOULD be written for easy understanding.

[GP] Follow Accepted ERC Standards
The Tested Code SHOULD conform to finalized [ERC] standards when it is reasonably capable of doing so for its use-case.

[GP] Define a Software License
The Tested Code SHOULD define a software license

[GP] Disclose New Vulnerabilities Responsibly
Security vulnerabilities that are not addressed by this specification SHOULD be brought to the attention of the Working Group and others through responsible disclosure as described in § 1.4 Feedback and new vulnerabilities.

[GP] Use Fuzzing
Fuzzing SHOULD be used to probe Tested Code for errors.

[GP] Use Mutation Testing
Mutation Testing SHOULD be used to evaluate and improve the quality of test suites for smart contracts.

[GP] Use Formal Verification
The Tested Code SHOULD undergo formal verification.

[GP] Select an Appropriate Threshold for Multisig Wallets
Multisignature requirements for privileged actions SHOULD have a sufficient number of signers, and NOT require “1 of N” nor all signatures.

B. References 引用

B.1 Normative references

[c-e-i]
Security Considerations - Solidity Documentation. Section ‘Use the Checks-Effects-Interactions Pattern’. Ethereum Foundation. URL: https://docs.soliditylang.org/en/latest/security-considerations.html#use-the-checks-effects-interactions-pattern
[CVE-2021-42574]
National Vulnerability Database CVE-2021-42574. The National Institute of Standards (US Department of Commerce). URL: https://nvd.nist.gov/vuln/detail/CVE-2021-42574
[CWE]
Common Weakness Enumeration. MITRE. URL: https://cwe.mitre.org/index.html
[CWE-1339]
CWE-1339: Insufficient Precision or Accuracy of a Real Number. MITRE. URL: https://cwe.mitre.org/data/definitions/1339.html
[CWE-252]
CWE-252: Unchecked Return Value. MITRE. URL: https://cwe.mitre.org/data/definitions/252.html
[CWE-284]
CWE-284: Improper Access Control. MITRE. URL: https://cwe.mitre.org/data/definitions/284.html
[CWE-573]
CWE-573: Improper Following of Specification by Caller. MITRE. URL: https://cwe.mitre.org/data/definitions/573.html
[CWE-667]
CWE-667: Improper Locking. MITRE. URL: https://cwe.mitre.org/data/definitions/667.html
[CWE-670]
CWE-670: Always-Incorrect Control Flow Implementation. MITRE. URL: https://cwe.mitre.org/data/definitions/670.html
[CWE-94]
CWE-94: Improper Control of Generation of Code (‘Code Injection’). MITRE. URL: https://cwe.mitre.org/data/definitions/94.html
[DevCon-rounding]
Tackling Rounding Errors with Precision Analysis. Raoul Schaffranek. Ethereum Foundation. URL: https://archive.devcon.org/archive/watch/6/tackling-rounding-errors-with-precision-analysis/?tab=YouTube
[DevCon-siphoning]
The Gas Siphon Attack: How it Happened and How to Protect Yourself. Shane Fontaine. Ethereum Foundation. URL: https://archive.devcon.org/archive/watch/5/the-gas-siphon-attack-how-it-happened-and-how-to-protect-yourself/?tab=YouTube
[EF-SL]
Specification languages for creating formal specifications. Ethereum Foundation. URL: https://ethereum.org/en/developers/docs/smart-contracts/formal-verification/#specification-languages
[EIP]
EIP-1: EIP Purpose and Guidelines. Ethereum Foundation. URL: https://eips.ethereum.org/EIPS/eip-1
[EIP-155]
Simple Replay Attack Protection. Vitalik Buterin. Ethereum Foundation. URL: https://eips.ethereum.org/EIPS/eip-155
[EIP-6049]
Deprecate SELFDESTRUCT. William Entriken. Ethereum Foundation. URL: https://eips.ethereum.org/EIPS/eip-6049
[ERC]
ERC Final - Ethereum Improvement Proposals. Ethereum Foundation. URL: https://eips.ethereum.org/erc
[ERC137]
ERC-137: Ethereum Domain Name Service - Specification. Ethereum Foundation. URL: https://eips.ethereum.org/EIPS/eip-137
[ERC20]
EIP-20: Token Standard. Ethereum Foundation. URL: https://eips.ethereum.org/EIPS/eip-20
[ERC721]
ERC 721: Non-fungible Token Standard. Ethereum Foundation. URL: https://eips.ethereum.org/EIPS/eip-721/
[error-handling]
Control Structures - Solidity Documentation. Section ‘Error handling: Assert, Require, Revert and Exceptions’. Ethereum Foundation. URL: https://docs.soliditylang.org/en/v0.8.14/control-structures.html#error-handling-assert-require-revert-and-exceptions
[ET-tools]
EEA EthTrust Tool Implementation Registry. EEA. URL: https://github.com/EntEthAlliance/ethtrust-tool-registry/
[EthTrust-sl-v1]
EEA EthTrust Security Levels Specification. Version 1. Enterprise Ethereum Alliance. URL: https://entethalliance.org/specs/ethtrust-sl/v1/
[EVM-version]
Using the Compiler - Solidity Documentation. (§Targets). Ethereum Foundation. URL: https://docs.soliditylang.org/en/latest/using-the-compiler.html#target-options
[freipi]
You’re writing require statements wrong. Brock Elmore. Nascent. URL: https://www.nascent.xyz/idea/youre-writing-require-statements-wrong
[GDPR]
Regulation (EU) 2016/679 of the European Parliament and of the Council of 27 April 2016 on the protection of natural persons with regard to the processing of personal data and on the free movement of such data, and repealing Directive 95/46/EC (General Data Protection Regulation) (Text with EEA relevance). The European Union. URL: https://eur-lex.europa.eu/legal-content/EN/TXT/PDF/?uri=CELEX:32016R0679
[hash-commit]
Commitment scheme - WikiPedia. WikiMedia Foundation. URL: https://en.wikipedia.org/wiki/Commitment_scheme
[Ivanov]
Targeting the Weakest Link: Social Engineering Attacks in Ethereum Smart Contracts. Nikolay Ivanov; Jianzhi Lou; Ting Chen; Jin Li; Qiben Yan. URL: https://arxiv.org/pdf/2105.00132.pdf#subsection.4.2
[NatSpec]
NatSpec Format - Solidity Documentation. Ethereum Foundation. URL: https://docs.soliditylang.org/en/latest/natspec-format.html
[Ownable]
ERC-173: Contract Ownership Standard. Ethereum Foundation. URL: https://eips.ethereum.org/EIPS/eip-173
[RBAC]
INCITS 359-2012: Information Technology - Role Based Access Control. InterNational Committee for Information Technology Standards. URL: http://www.techstreet.com/products/1837530
[RFC2119]
Key words for use in RFCs to Indicate Requirement Levels. S. Bradner. IETF. March 1997. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc2119
[RFC8174]
Ambiguity of Uppercase vs Lowercase in RFC 2119 Key Words. B. Leiba. IETF. May 2017. Best Current Practice. URL: https://www.rfc-editor.org/rfc/rfc8174
[rounding-errors]
Formal Specification of Constant Product (x × y = k) Market Maker Model and Implementation. Runtime Verification. URL: https://github.com/runtimeverification/verified-smart-contracts/blob/uniswap/uniswap/x-y-k.pdf
[SHA3-256]
FIPS 202 - SHA-3 Standard: Permutation-Based Hash and Extendable-Output Functions. The National Institute of Standards (US Department of Commerce). URL: http://dx.doi.org/10.6028/NIST.FIPS.202
[software-license]
Choosing an Open Source License. GitHub. URL: https://choosealicense.com/
[solidity-alerts]
Solidity Blog - Security Alerts. Ethereum Foundation. URL: https://blog.soliditylang.org/category/security-alerts/
[solidity-bugs]
List of Known Bugs. Ethereum Foundation. URL: https://github.com/ethereum/solidity/blob/develop/docs/bugs.rst
[solidity-bugs-json]
A JSON-formatted list of some known security-relevant Solidity bugs. Ethereum Foundation. URL: https://github.com/ethereum/solidity/blob/develop/docs/bugs.json
[solidity-cheatsheet]
Solidity Documentation: Cheatsheet - Order Of Precedence Of Operators. Ethereum Foundation. URL: https://docs.soliditylang.org/en/latest/cheatsheet.html#order-of-precedence-of-operators
[solidity-events]
Solidity Documentation: Contracts - Events. Ethereum Foundation. URL: https://docs.soliditylang.org/en/latest/contracts.html#events
[solidity-patterns]
Solidity Patterns. Franz Volland. URL: https://fravoll.github.io/solidity-patterns/
[solidity-release-818]
Solidity 0.8.18 Release Announcement. Ethereum Foundation. 2023-02-01. URL: https://soliditylang.org/blog/2023/02/01/solidity-0.8.18-release-announcement/
[solidity-security]
Security Considerations - Solidity Documentation.. Ethereum Foundation. URL: https://docs.soliditylang.org/en/latest/security-considerations.html
[Solidity-Style-Guide]
Solidity Style Guide - Solidity Documentation. URL: https://docs.soliditylang.org/en/latest/style-guide.html
[solidity-underhanded-richards2022]
Solidity Underhanded Contest 2022. Submission 9 - Tynan Richards. Ethereum Foundation. URL: https://github.com/ethereum/solidity-underhanded-contest/tree/master/2022/submissions_2022/submission9_TynanRichards
[swcregistry]
Smart Contract Weakness Classification Registry. ConsenSys Diligence. URL: https://swcregistry.io
[TimeLock]
Protect Your Users With Smart Contract Timelocks. OpenZeppelin. URL: https://blog.openzeppelin.com/protect-your-users-with-smart-contract-timelocks/
[tmio-bp]
Best Practices for Smart Contracts (privately made available to EEA members). TMIO. URL: https://github.com/EntEthAlliance/eta-registry/blob/master/working-docs/tmio-bp.md
[token-standards]
Ethereum Development Documentation - Token Standards. Ethereum Foundation. URL: https://ethereum.org/en/developers/docs/standards/tokens/
[unicode-bdo]
How to use Unicode controls for bidi text. W3C Internationalization. 10 March 2016. URL: https://www.w3.org/International/questions/qa-bidi-unicode-controls
[unicode-blocks]
Blocks-14.0.0.txt. Unicode®, Inc. 22 January 2021. URL: https://www.unicode.org/Public/UNIDATA/Blocks.txt
B.2 Informative references
[ASVS]
OWASP Application Security Verification Standard. The OWASP Foundation. URL: https://github.com/OWASP/ASVS
[certik-rari]
Fei Protocol Incident Analysis. CertiK. URL: https://certik.medium.com/fei-protocol-incident-analysis-8527440696cc
[chase]
Malleable Signatures: New Definitions and Delegatable Anonymous Credentials. Melissa Chase; Markulf Kohlweiss; Anna Lysyanskaya; Sarah Meiklejohn. URL: https://smeiklej.com/files/csf14.pdf
[EEA-L2]
Introduction to Ethereum Layer 2. Enterprise Ethereum Alliance, Inc. URL: https://entethalliance.org/eea-primers/entry/5696/
[EF-MEV]
Maximal Extractable Value (MEV). Ethereum Foundation. URL: https://ethereum.org/en/developers/docs/mev/
[EIP-3529]
Reduction in Refunds. Ethereum Foundation. URL: https://eips.ethereum.org/EIPS/eip-3529
[futureblock]
Future-block MEV in Proof of Stake. Ethereum Foundation. URL: https://archive.devcon.org/archive/watch/6/future-block-mev-in-proof-of-stake/?tab=YouTube
[License]
Apache license version 2.0. The Apache Software Foundation. URL: http://www.apache.org/licenses/LICENSE-2.0
[postmerge-mev]
Why is Oracle Manipulation after the Merge so cheap? Multi-Block MEV. ChainSecurity. URL: https://chainsecurity.com/oracle-manipulation-after-merge/
[solidity-reports]
Reporting a Vulnerability, in Security Policy. Ethereum Foundation. URL: https://github.com/ethereum/solidity/security/policy#reporting-a-vulnerability
[WSE]
Symbolic Execution. WikiPedia. URL: https://en.wikipedia.org/wiki/Symbolic_execution

B.2 Informative references

[ASVS]
OWASP Application Security Verification Standard. The OWASP Foundation. URL: https://github.com/OWASP/ASVS
[certik-rari]
Fei Protocol Incident Analysis. CertiK. URL: https://certik.medium.com/fei-protocol-incident-analysis-8527440696cc
[chase]
Malleable Signatures: New Definitions and Delegatable Anonymous Credentials. Melissa Chase; Markulf Kohlweiss; Anna Lysyanskaya; Sarah Meiklejohn. URL: https://smeiklej.com/files/csf14.pdf
[EEA-L2]
Introduction to Ethereum Layer 2. Enterprise Ethereum Alliance, Inc. URL: https://entethalliance.org/eea-primers/entry/5696/
[EF-MEV]
Maximal Extractable Value (MEV). Ethereum Foundation. URL: https://ethereum.org/en/developers/docs/mev/
[EIP-3529]
Reduction in Refunds. Ethereum Foundation. URL: https://eips.ethereum.org/EIPS/eip-3529
[futureblock]
Future-block MEV in Proof of Stake. Ethereum Foundation. URL: https://archive.devcon.org/archive/watch/6/future-block-mev-in-proof-of-stake/?tab=YouTube
[License]
Apache license version 2.0. The Apache Software Foundation. URL: http://www.apache.org/licenses/LICENSE-2.0
[postmerge-mev]
Why is Oracle Manipulation after the Merge so cheap? Multi-Block MEV. ChainSecurity. URL: https://chainsecurity.com/oracle-manipulation-after-merge/
[solidity-reports]
Reporting a Vulnerability, in Security Policy. Ethereum Foundation. URL: https://github.com/ethereum/solidity/security/policy#reporting-a-vulnerability
[WSE]
Symbolic Execution. WikiPedia. URL: https://en.wikipedia.org/wiki/Symbolic_execution

  • Title: EEA以太坊企业联盟安全规范第三版
  • Author: Kopei
  • Created at : 2025-08-03 13:01:59
  • Updated at : 2025-08-11 11:30:01
  • Link: https://kopei.github.io/2025/08/03/EEA以太坊企业联盟安全规范第三版/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments