当前位置:首页 > 物联网 > 区块链
[导读] 本次教程主要展示在编写智能合约时通常应遵循的安全模式。 方案建议 以下建议适用于以太坊上任何智能合约系统的开发。 外部调用 使用外部调用时需要格外注意 调

本次教程主要展示在编写智能合约时通常应遵循的安全模式。

方案建议

以下建议适用于以太坊上任何智能合约系统的开发。

外部调用

使用外部调用时需要格外注意

调用不受信任的智能合约可能会带来一些意外的风险或Bug。外部调用可能在该合约或它依赖的任何其他合约中执行恶意代码。因此,每个外部调用都应视为潜在的安全风险。 如果无法或不希望删除外部调用,请使用本节教程的建议将危险降至最低。

标记不受信任的合约

当与外部合约进行交互时,请以清楚表明与它们进行交互不安全的方式命名变量,方法和合约接口,适用于您自己的调用外部合约的函数。

// bad

Bank.withdraw(100); // Unclear whether trusted or untrusted

function makeWithdrawal(uint amount) { // Isn‘t clear that this funcTIon is potenTIally unsafe

Bank.withdraw(amount);

}

// good

UntrustedBank.withdraw(100); // untrusted external call

TrustedBank.withdraw(100); // external but trusted bank contract maintained by XYZ Corp

funcTIon makeUntrustedWithdrawal(uint amount) {

UntrustedBank.withdraw(amount);

}

避免外部调用后的状态更改

无论使用原始调用(形式为someAddress.call())还是合约调用(形式为ExternalContract.someMethod()),都可能存在执行恶意代码的风险。 即使ExternalContract不是恶意的,恶意代码也可以通过其调用的任何合约执行。

一种特别的危险是恶意代码可能会劫持控制流,从而导致由于可重入而产生的漏洞。

如果要调用不受信任的外部合约,请避免在调用后更改状态。这种模式有时也被称为检查效果交互模式。

避免使用transfer()和send()

.transfer()和.send()都会将2300gas转发给收件人。这一硬编码gas津贴的目的是防止重入漏洞,但这只有在gas成本不变的假设下才有意义。最近的EIP 1283(在最后一刻退出了君士坦丁堡硬叉)和EIP 1884(预计将在伊斯坦布尔硬叉中到达)表明此假设无效。

为了避免将来gas成本发生变化时会产生问题,最好改用.call.value(amount)(“”)。请注意,这无助于减轻重入攻击,因此必须采取其他预防措施。

处理外部调用中的Bug

Solidity提供了适用于原始地址的低级调用方法:address.call(),address.callcode(),address.delegatecall()和address.send()。 这些低级方法从不抛出异常,但是如果调用遇到异常,则将返回false。 另一方面,合同调用(例如,ExternalContract.doSomething())将自动传播一个引发(例如,如果doSomething()引发,则ExternalContract.doSomething()也将引发)。

如果选择使用低级调用方法,请确保通过检查返回值来处理调用失败的可能性。

// bad

someAddress.send(55);

someAddress.call.value(55)(“”); // this is doubly dangerous, as it will forward all remaining gas and doesn’t check for result

someAddress.call.value(100)(bytes4(sha3(“deposit()”))); // if deposit throws an excepTIon, the raw call() will only return false and transaction will NOT be reverted

// good

(bool success, ) = someAddress.call.value(55)(“”);

if(!success) {

// handle failure code

}

ExternalContract(someAddress).deposit.value(100)();

支持外部调用push

外部调用可能发生意外或者恶意BUG。为了最大限度地减少此类故障造成的损害,通常最好将每个外部调用隔离到自己的事务中,该事务可以由调用的接收者发起。这与支付尤其相关,在支付中,最好让用户提取资金,而不是自动向他们推送资金。(这也降低了GAS限制出现问题的可能性)避免在一个事务中合并多个以太坊转移。

// bad

contract auction {

address highestBidder;

uint highestBid;

function bid() payable {

require(msg.value 》= highestBid);

if (highestBidder != address(0)) {

(bool success, ) = highestBidder.call.value(highestBid)(“”);

require(success); // if this call consistently fails, no one else can bid

}

highestBidder = msg.sender;

highestBid = msg.value;

}

}

// good

contract auction {

address highestBidder;

uint highestBid;

mapping(address =》 uint) refunds;

function bid() payable external {

require(msg.value 》= highestBid);

if (highestBidder != address(0)) {

refunds[highestBidder] += highestBid; // record the refund that this user can claim

}

highestBidder = msg.sender;

highestBid = msg.value;

}

function withdrawRefund() external {

uint refund = refunds[msg.sender];

refunds[msg.sender] = 0;

(bool success, ) = msg.sender.call.value(refund)(“”);

require(success);

}

}

不要将调用委托给不受信任的代码

delegateCall函数用于从其他合约调用函数,就好像它们属于调用方合约一样。因此调用方可以改变调用地址的状态,这是存在风险。下面的示例演示了使用delegatecall如何导致合约的破坏和资金损失。

contract Destructor

{

function doWork() external

{

selfdestruct(0);

}

}

contract Worker

{

function doWork(address _internalWorker) public

{

// unsafe

_internalWorker.delegatecall(bytes4(keccak256(“doWork()”)));

}

}

如果使用已部署的Destructor合约的地址作为参数调用Worker.doWork(),则Worker合约将自毁。 仅将执行委托给受信任的合约,而不委托给用户提供的地址。

不要假设合约是用零余额创建的,攻击者可以在创建合约之前将以太坊发送到该合约的地址。

请记住,可以强制将以太坊发送到一个帐户

小心编写严格检查智能合约的余额的不变量。

攻击者可以强行将以太坊发送到任何帐户,并且这是无法避免的(即使使用执行revert()的回退函数也无法阻止)。

攻击者可以通过创建合约,用1 wei资助该合约并调用selfdestruct(victimAddress)来实现此目的。在victimaddress中没有调用任何代码,因此无法阻止它。发送到矿工的地址的区块奖励也是如此,该地址可以是任意地址。

此外,由于可以预先计算合约地址,因此可以在部署合约之前将以太坊发送到某个地址。

请记住,链上数据是公开的

许多应用程序要求提交的数据在某个时间点之前都是隐匿的。游戏(如链上剪刀石头布)和拍卖机制(如竞价拍卖)两大类例子。如果您在构建隐私问题的应用程序,请确保避免用户过早公布信息。最好的策略是使用具有不同阶段的承诺方案:首先使用值的哈希值进行提交,然后在后续阶段中显示值。

例子:

在剪刀石头布上,要求两个玩家先提交其预期动作的哈希值,然后要求两个玩家均提交其动作;如果提交的动作与散列不匹配,则将其丢弃。

在拍卖中,要求玩家在初始阶段提交其出价值的哈希值(以及大于其出价值的保证金),然后在第二阶段提交其拍卖出价。

开发依赖于随机数生成器的应用程序时,顺序应始终为(1)玩家提交动作,(2)生成随机数,(3)玩家支付。产生随机数的方法本身就是积极研究的领域。当前同类最佳的解决方案包括比特币区块头(通过http://btcrelay.org验证),哈希提交显示方案(即,一方生成数字,发布其哈希值以“提交”给该值,以及然后显示价值)和RANDAO。由于以太坊是确定性协议,因此协议中的任何变量都不能用作不可预测的随机数。还应注意,矿工在某种程度上控制着block.blockhash()值*。

注意某些参与者可能“下线”而不上线的可能性

不要依赖于由特定方执行特定操作的退款或索赔程序,而没有其他方法将资金取出。例如在石头剪刀布游戏中,一个常见的错误是在两个玩家都提交动作之前不进行支付。 但是恶意的玩者可以通过根本不提交自己的举动来“困扰”对方-实际上,如果一个玩者看到了对方显示的举动并确定自己输了,则根本没有理由提出自己的举动。

(1)提供一种规避未参与参与者的方法,可能会在一定时限内进行;

(2)考虑为参与者在其所处的所有情况下提交信息提供额外的经济激励。

注意负整数取反

solidity提供了几种处理有符号整数的类型。与大多数编程语言一样,在solidity中,带n位的有符号整数可以表示从-2^(n-1)到2^(n-1)-1的值。这意味着MIN_INT没有正等价物。求反是通过找到一个数字的两个补数实现的,因此,最负数的求反将得出相同的值。

contract Negation {

function negate8(int8 _i) public pure returns(int8) {

return -_i;

}

function negate16(int16 _i) public pure returns(int16) {

return -_i;

}

int8 public a = negate8(-128); // -128

int16 public b = negate16(-128); // 128

int16 public c = negate16(-32768); // -32768

}

处理此问题的一种方法是,在求反之前检查变量的值,如果该值等于最小整数,则抛出。另一种选择是确保使用容量更大的类型(例如int32而不是int16)永远不会达到最大负数。

当min_int乘以或除以-1时,int类型也会出现类似的问题。

来源: 区块链研究实验室

本站声明: 本文章由作者或相关机构授权发布,目的在于传递更多信息,并不代表本站赞同其观点,本站亦不保证或承诺内容真实性等。需要转载请联系该专栏作者,如若文章内容侵犯您的权益,请及时联系本站删除。
换一批
延伸阅读

LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: 驱动电源

在工业自动化蓬勃发展的当下,工业电机作为核心动力设备,其驱动电源的性能直接关系到整个系统的稳定性和可靠性。其中,反电动势抑制与过流保护是驱动电源设计中至关重要的两个环节,集成化方案的设计成为提升电机驱动性能的关键。

关键字: 工业电机 驱动电源

LED 驱动电源作为 LED 照明系统的 “心脏”,其稳定性直接决定了整个照明设备的使用寿命。然而,在实际应用中,LED 驱动电源易损坏的问题却十分常见,不仅增加了维护成本,还影响了用户体验。要解决这一问题,需从设计、生...

关键字: 驱动电源 照明系统 散热

根据LED驱动电源的公式,电感内电流波动大小和电感值成反比,输出纹波和输出电容值成反比。所以加大电感值和输出电容值可以减小纹波。

关键字: LED 设计 驱动电源

电动汽车(EV)作为新能源汽车的重要代表,正逐渐成为全球汽车产业的重要发展方向。电动汽车的核心技术之一是电机驱动控制系统,而绝缘栅双极型晶体管(IGBT)作为电机驱动系统中的关键元件,其性能直接影响到电动汽车的动力性能和...

关键字: 电动汽车 新能源 驱动电源

在现代城市建设中,街道及停车场照明作为基础设施的重要组成部分,其质量和效率直接关系到城市的公共安全、居民生活质量和能源利用效率。随着科技的进步,高亮度白光发光二极管(LED)因其独特的优势逐渐取代传统光源,成为大功率区域...

关键字: 发光二极管 驱动电源 LED

LED通用照明设计工程师会遇到许多挑战,如功率密度、功率因数校正(PFC)、空间受限和可靠性等。

关键字: LED 驱动电源 功率因数校正

在LED照明技术日益普及的今天,LED驱动电源的电磁干扰(EMI)问题成为了一个不可忽视的挑战。电磁干扰不仅会影响LED灯具的正常工作,还可能对周围电子设备造成不利影响,甚至引发系统故障。因此,采取有效的硬件措施来解决L...

关键字: LED照明技术 电磁干扰 驱动电源

开关电源具有效率高的特性,而且开关电源的变压器体积比串联稳压型电源的要小得多,电源电路比较整洁,整机重量也有所下降,所以,现在的LED驱动电源

关键字: LED 驱动电源 开关电源

LED驱动电源是把电源供应转换为特定的电压电流以驱动LED发光的电压转换器,通常情况下:LED驱动电源的输入包括高压工频交流(即市电)、低压直流、高压直流、低压高频交流(如电子变压器的输出)等。

关键字: LED 隧道灯 驱动电源
关闭