我是刚接触过稳健性的人,但我多次读到应该避免使用tx.origin &应该使用msg.sender。在solidity 页面中有一个给出的演示。上面写着:-
不要使用tx.origin进行授权。假设你有这样的钱包合同:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
// THIS CONTRACT CONTAINS A BUG - DO NOT USE
contract TxUserWallet {
address owner;
constructor() {
owner = msg.sender;
}
function transferTo(address payable dest, uint amount) public {
// THE BUG IS RIGHT HERE, you must use msg.sender instead of tx.origin
require(tx.origin == owner);
dest.transfer(amount); // .transfer is a global variable
}
}现在有人骗你把以太送到这个攻击钱包的地址:
// SPDX-License-Identifier: GPL-3.0
pragma solidity >=0.7.0 <0.9.0;
interface TxUserWallet {
function transferTo(address payable dest, uint amount) external;
}
contract TxAttackWallet {
address payable owner;
constructor() {
owner = payable(msg.sender);
}
receive() external payable {
TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance); // **LINE 1**
}
}现在,我想知道第1行将如何从TxUserWallet中抽走全部资金。我认为transfer()是一个全局变量,它将在攻击钱包的dest中传递要处理的数量。.transfer()将如何触发合同TxAttackWallet的receive()函数。其次,在TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);行中,为什么我们要将它写成TxUserWallet(msg.sender),比如为什么我们在合同名称之后添加(msg.sender),以及通过编写msg.sender.balance传递什么值?
发布于 2022-08-31 13:28:52
您应该避免使用tx.origin来防止重入攻击。固体重入攻击解释
基本上,攻击者正在递归地从合同中提取资金,直到余额为0。所以有一个函数调用链。重入攻击涉及两个智能契约。易受攻击的契约和不受信任的攻击者的契约。(fallback函数是在多个条件下调用的,这使得编写每个条件的代码变得困难。这就是为什么添加receive的原因。receive被称为只有契约接收以太,因此只有在合同中没有实现函数签名时,才应该执行回退。)

msg.sender是最后一个调用者,而tx.origin是这些递归函数链的原始调用者。
funcA => funcB => funcC
msg.sender是funcC的调用者,而tx.origin是funcA的调用者
现在,为了防止重入攻击,我们必须确保没有函数调用链。
require(tx.origin == msg.sender)在你的问题中,故事有点不同。想象一下工作流。你是说
现在有人骗你把以太送到这个攻击钱包的地址:
您正在向TxAttackWallet发送资金,因此您正在启动函数链。tx.origin是你,老板,tx.origin == owner。一旦您将钱发送到TxAttackWallet,将自动调用receive of TxAttackWallet,因此这段代码将被递归执行。你在循环中
TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);你一直把资金从你的合同转到TxUserWallet。
谁是tx.origin?这是因为您启动了这个函数链,所以这将始终通过:
require(tx.origin == owner); 如果您使用的是msg.sender==owner,msg.sender将是TxUserWallet,因此要求条件不会通过,也不会转移资金。
发布于 2022-08-31 11:26:04
.transfer()将如何触发合同TxAttackWallet的receive()功能
在不指定receive()字段的情况下,当您将ETH发送到契约地址时,会自动调用data函数。
不可能使用.transfer()函数指定数据字段,为此需要使用低级别的.call()。
医生:
receive() - https://docs.soliditylang.org/en/v0.8.16/contracts.html#receive-ether-function.transfer()和.call() - https://docs.soliditylang.org/en/v0.8.16/types.html#members-of-addresses为什么我们把它写成
TxUserWallet(msg.sender),就像为什么我们在合同名称之后添加(msg.sender),以及通过编写msg.sender.balance传递什么值一样。
TxUserWallet(msg.sender)是指向msg.sender地址(在您的例子中是TxUserWallet契约地址)的契约类型指针,期望它实现TxUserWallet的所有public和external函数。
这样,TxAttackWallet契约期望能够在msg.sender上调用transferTo()函数,并期望接收空响应,因为transferTo()函数不返回任何params。
msg.sender.balance返回本机余额(ETH上的ETH,Binance智能链上的BNB,.)对于msg.sender,在您的例子中,这是TxUserWallet契约地址。
医生:
TxUserWallet(msg.sender) - https://docs.soliditylang.org/en/v0.8.16/types.html#contract-types.balance - https://docs.soliditylang.org/en/v0.8.16/units-and-global-variables.html#members-of-address-typeshttps://stackoverflow.com/questions/73554510
复制相似问题