BNB Chain是Web3世界中最受欢迎的区块链之一,其费用合理、交易迅速以及项目生态系统丰富几大原因吸引了广大用户。与任何的区块链都一样,BNB Chain上的开发者在开发过程中首先考虑的应该是安全问题:因为任何资金的损失都会导致用户对协议及平台的信心减弱,而安全漏洞和黑客攻击是开发者面临的最大风险之一。
在本文中,我们提供了十大实用安全tips,让开发者可以在BNB Chain上减少风险并开发更安全的智能合约。
一. 防止重放攻击
01 ╱ 定义 ╱
重放攻击(Replay Attacks)又称重播攻击、回放攻击,是区块链环境中一种常见的攻击类型。在网络安全中,“重放攻击"”是一种网络攻击类型,其中有效的数据传输被恶意或欺诈性地重复或延迟。
在Web3和智能合约的环境背景下,这通常意味着攻击者能够在未经原始用户允许的情况下重复智能合约中的交易或操作。这可能会导致各种形式的欺诈,如双重支付等。
这些攻击会给用户和开发者带来严重的后果,因为它们允许攻击者重复使用相同的签名,以获得智能合约上所有资金或其他资产的未授权访问。
为了防止重放攻击,开发人员必须仔细设计和实现他们的智能合约代码,并遵循签名验证以及行业最佳安全标准。
02 ╱ 案例解析 ╱
以下代码片段表示了BNB链上一个代币的转移transfer实现过程。该代码很容易受到重放攻击,从而使得攻击者能够重复使用同一个签名。
这个功能缺乏nonce或重放保护,允许攻击者多次“重放”一个已签名的转账交易。攻击者可以拦截已签署的交易,并再次将其发送到同一个合约或另一个合约,而合约仍会认为它是有效的,因此攻击者就可能利用该漏洞窃取资产。
03 ╱ 改进方法 ╱
在签名中增加一个nonce或使用mapping变量来记录签名。其中更具地的解决方案会根据项目设计的不同而不同。
二. 谨防重入攻击
01 ╱ 定义 ╱
当一个恶意合约在初始调用完成之前反复调用一个易受攻击的合约时,就会发生重入攻击。换句话说,攻击者可以“欺骗”一个易受攻击的合约,使其认为它已经完成了一个交易并可以自由地进入下一个交易,但实际上它仍然在执行攻击者的恶意代码。
这可能导致攻击者能够以意想不到的方式操纵合约的状态,并有可能获得未经授权的资金。
02 ╱ 案例解析 ╱
在下面的代码片段中,用户可以通过调用提款withdraw函数并指定他们要提取的金额来从他们的账户中提取资金。然而,由于withdraw函数没有防止对该函数的递归调用,因此很容易遭到重入攻击,
攻击者可以通过创建一个恶意合约,在余额真正从其账户扣除之前多次调用withdraw函数来利用漏洞。函数msg.sender.call向恶意合约发送资金,攻击者通过恶意合约的receive()函数在其余额减少到零之前反复提取资金,从而耗尽受害者合约的所有资金。
03 ╱ 改进方法 ╱
在任何外部调用之前进行状态更新。
该模式被称为“检查-影响-交互(Check-Effects-Interact)”模式,是一种用于防止智能合约中重入攻击的设计模式。该模式通过先检查前提条件,然后在进行任何外部调用之前更新状态,将状态变化与其他合约的外部调用分开。这样一来,如果一个外部调用触发了一个试图回调到合约的回调,但状态已经被更新了,因此可以防止其他意外影响。
通过遵循这种模式,开发者可以确保他们的合约更安全,更不容易受到重入式攻击。
另一个可能的修复方法则是利用modifier来限制对同一函数的多次调用,这很像OpenZeppelin的ReentrancyGuard。
三. 慎待预言机
01 ╱ 定义 ╱
预言机可以帮助智能合约从区块链外部检索信息。通常去中心化的交易所(DEX)资产的价格是由预言机从DEX最后一次成功交易中提取出的价格而决定的。
然而问题来了,该价格可能被任何人轻易操纵,导致智能合约出现问题。我们经常会看到通过闪电贷操控价格预言机的案例,原因是闪电贷允许用户只要在同一区块内偿还贷款,就可以无抵押借入巨额资金。这样攻击者就可以很容易地影响甚至操控价格,从虚假清算、过度贷款或不公平交易中获利。
02 ╱ 案例解析 ╱
以下是一个容易受到预言机操纵攻击的代码片段。
该合约允许用户使用路由合约将代币A换成代币B,但它依赖外部预言机(Uniswap Pair合约)来获得代币A和代币B的储备以计算价格。攻击者能够操纵Uniswap Pair合约的储备以及getAmountOut函数,最终导致交换以不合理的价格执行。
03 ╱ 改进方法 ╱
为了防止这种攻击,开发者应该使用可获取链上中心化和去中心化交易所成交量加权平均价格(VWAP)或时间加权平均价格(TWAP)的去中心化预言机网络。这样一来,数据将从多个数据源和广泛的时间内进行收集,从而使代码更不容易受到攻击和操纵的影响。对于开发者来说,能够删除智能合约中任何操纵预言机的攻击载体以防止潜在漏洞十分重要。
四. 设置函数可见性
01 ╱ 定义 ╱
正确设置函数的可见性可确保智能合约的安全性和完整性。使用不正确的函数可见性设置会允许非预期用户操纵合约状态,从而导致资金被盗或合约功能被控制等严重问题。
通过将函数的可见性设置为私有或内部,可确保开发者对某些函数的访问有一定限制,仅有授权方才可以调用这些函数。私有函数只能从合约本身调用,而内部函数也可以从沿用当前合约中调用。这允许开发者在保持控制访问功能权限的同时可创建具有更强大功能且更复杂的合约。
02 ╱ 案例解析 ╱
函数setAdmin()允许任何人将任何地址设置为合约管理员。根据合约内授予管理地址的权限,这有可能导致开发者失去对合约本身的控制甚至资金造成损失。
通过将函数的可见性设置为internal内部,某些合约函数在内部就可能允许将某些用户设置为管理员。
03 ╱ 改进方法 ╱
访问modifier是一个重要的安全功能,它可以规定谁可以访问合约中的特定功能或变量。这些modifier可被用来限制某些函数或变量对特定角色或地址的可见性,并防止恶意行为者未经授权进行访问或操纵合约状态。
例如,某合约可能有一个只有合约所有者才可以调用的函数,或者有一个只能由一组特定地址访问的变量。
通过将可见性modifier改成external,并将访问modifier设置为onlyOwner,可将对setAdmin函数的访问限制在合约所有者的地址当中。这将防止恶意的外部方控制某些特权功能。
正确使用可见性和限制性modifier可以令合约管理变得更加容易,也可以减少常见的如重入式攻击(攻击者反复调用一个函数来操纵合约状态)或前置运行攻击(攻击者监控未决交易并在合法交易执行前操纵合约状态)而带来的问题。
通过适当地使用这些功能,开发人员可以增强他们的合约安全性和可靠性,减少未经授权访问的风险,并提高他们代码整体质量和可维护性。
五. 警惕合约可升级性
01 ╱ 定义 ╱
在决定未来是否要对合约升级性时,初始时就要仔细考虑合约的设计。合约可升级性是指在智能合约被部署到区块链上后,具有可被修改或更新其逻辑的性质。虽然可升级性可以提供许多优势(如修复错误,提高效率或增加新的功能等),但它同时也引入了一些风险。合约升级可能导致引入新的漏洞、增加合约复杂性,或造成意外的后果。
因为代理管理员可以在没有得到社区共识的情况下升级合约,因此合约可升级性也引发了信任问题。开发者需要仔细权衡可升级性的优势和劣势,并确定自己的项目是否真正有必要引入可升级合约。在某些情况下,设计一个从一开始就打定主意不可改变的合约而不是依赖以后的修改能力,会更加安全。
02 ╱ 改进方法 ╱
当涉及到合约的可升级性时,有几个重要的做法需要遵循。首先,最重要的是不要修改proxy library。Proxy合约库极具复杂性,特别是在存储管理和升级机制方面。即使是小错误也会大大影响Proxy和逻辑合约的运行。事实上,在审计过程中发现的许多与proxy相关的严重性错误都是由不恰当修改proxy库造成的。
合约可升级性的另一个关键做法是在基本合约中包含一个存储“间隙”。逻辑合约则必须在合约代码中包含一个存储间隙,以考虑到在部署新的逻辑实现时可能引入的新变量。在添加新的状态变量后,更新“间隙”的大小就变得更加重要了。这种做法可以确保未来的升级能够顺利进行,不会出现问题。
最后,必须避免使用selfdestruct()或对不受信任的合约执行delegatecall()/ call()。攻击者可以利用这些函数来破坏逻辑实现或执行自定义逻辑。为了防止这种情况,验证用户的输入很重要!不要允许合约对不受信的合约执行delegatecall()/ call()。此外,不建议在逻辑合约中使用delegatecall(),因为在多个合约中管理存储布局会很困难。通过遵循这些做法,开发者可以在他们的合约升级中最大限度地减少漏洞及风险。
六. 防止抢先交易
01 ╱ 定义 ╱
抢先交易(Front-Running),一直是一个顽固且不容易解决的问题,用户能够利用提交交易和区块链上确认交易之间的延迟来获利。这种延迟是由mempool造成的。
mempool是一个临时存储区,用于存储已广播到网络的未确认交易。网络中的所有节点都保持着一个mempool,允许任何人看到待定交易,并有可能拦截从而从中获利。mempool还为矿工提供了一个重新安排交易的机会,以使他们的利润最大化,创造所谓的矿工(或最大)可提取价值(MEV)。
02 ╱ 案例解析 ╱
下面是一个容易出现抢先交易的拍卖出价功能的例子。
这个功能允许用户在拍卖中出价,但它可能会受到抢先交易攻击。假设一个恶意用户监控区块链时,看到另一个用户已经提交了一个高价,于是这个恶意用户可以迅速提交一个更高的出价并被优先处理,最终赢得拍卖。
在以下版本中,用户提交不为人知的出价价格,而这些出价被存储在一个mapping中。出价金额在竞价期结束前都是加密的。
03 ╱ 改进方法 ╱
在竞价期结束时,用户可以通过提交原始竞价金额和一个秘密值来揭示他们的竞价。合约验证出价金额和保密的哈希值与存储的秘密出价相符,确保出价是在竞价期结束前提交的。如果该出价高于当前的最高出价,它就成为新的最高出价。通过在竞价期结束前掩盖出价金额,该功能可以缓释抢先交易攻击。
抢先交易和MEV已经成为区块链社区的主要关注点,各种解决方案,如交易和公平排序服务(FSS),已经被提出来解决这些问题。交易可以通过对其他用户隐藏交易细节,直到交易在区块链上执行,来帮助预防抢先交易。另一方面,FSS可以通过安全的链下交易排序来减少抢先交易和MEV的影响。
七. 制定安全响应计划
制定一个明确而全面的响应计划对于处理紧急安全事件至关重要。该计划应定期审查、更新,并测试其有效性。在发生紧急安全事件时,时间是最重要的。因此该计划应提前定制并包括识别、控制和缓释等细节操作步骤。
该计划应有效到位,让所有利益相关方了解情况。同时,定期备份数据对于防止数据丢失也很重要。计划中需概述将数据和系统恢复至从前状态的恢复过程。团队成员应接受针对该计划的系统培训,以确保每个人都了解自己的角色和责任。
一个精心准备的响应计划或许不能亡羊补牢,但却可以将事件的影响降到最低,并保持与用户和利益相关者的信任度。
八. 进行常规审计
常规的代码审计对于维护应用程序的安全至关重要。与专门从事智能合约安全的专业审计师合作是开发过程中的一个重要步骤。审计人员将检查代码中的漏洞,并为提高整体安全性提供建议。
优先考虑和解决已发现的问题,并与审计人员保持开放的沟通,对于提高安全性至关重要。
除此之外,与审计人员的沟通也很关键。审计人员可以详细解释他们发现的问题及漏洞,并就如何解决该漏洞提供指导和实用性帮助。通过与专业审计机构/人员的合作,安全将“更上一层楼”。
对BNB链的开发者来说,进行常规审计是任何一个全面的安全策略都不可或缺的组成部分。主动识别和解决代码中的漏洞可以将安全漏洞的风险降到最低。
九. 善用漏洞赏金计划
使用赏金计划是激励社群白帽黑客报告项目代码中安全漏洞的一个有效方法。通过提供如代币或其他奖励等激励,任何项目都可以鼓励有经验的人检查代码并报告他们发现的任何潜在问题。
制定一个明确、透明的漏洞赏金计划十分重要。其中计划需要包含:发现哪些类型的漏洞有资格获得奖励,如何评估这些漏洞的价值等。同时,在漏洞赏金计划中加入有公信力的第三方可以帮助确保该计划的顺利运行和奖励的公平分配。
同时,拥有一个多样化的“赏金猎人小组”也很重要。不同的白帽黑客有着不同的擅长领域,可以专注于发现其他人可能错过的问题。
最后,一旦报告了漏洞,就必须迅速有效地采取行动来解决这些漏洞。赏金计划可以成为识别漏洞的有效工具,但必须由开发团队来实际修复问题才有意义。
十. 开展用户安全教育
对Web3用户进行安全性教育,是建立一个安全生态系统的关键步骤。保证客户的安全,就有助于保证平台的安全。用户应该接受教育,了解保护他们账户和敏感信息的最佳做法。
其中最重要的环节则是学会如何避免网络钓鱼诈骗。
网络钓鱼诈骗犯,会通过冒充合法网站或服务来欺骗用户,使用户泄露私钥或密码。CertiK建议用户在收到任何信息时,始终要仔细检查任何邮件、来源等正在使用的网站URL,并且永远不要在不信任的网站上输入私人密钥或密码。
而另一个重要部分则是拥有安全且强大的密码。CertiK在此建议用户为每个账户使用独特和复杂的密码,并避免在不同的服务中重复使用密码。同时,还应使用密码管理器或其他安全存储机制来安全地存储密码。
最后,保护私钥极为重要。私钥相当于用户的密码,应该在任何时候都保证不被其他人接触到。用户应避免与任何人分享他们的私钥,并将其存放在一个安全的地方。
总结
在BNB Chain上构建智能合约和dApps的开发者必须采取全面的安全方法来保证其用户资金和资产的安全。更重要的是,在处理安全漏洞时要主动出击,而不是被动应付;在漏洞发生时甚至之前就要制定计划,并对所有相关用户和利益相关者进行适当的安全教育。通过结合所有以上措施,可以帮助项目大大减少安全漏洞和黑客攻击的风险,, https://t.me/gtokentool 。