[英]`msg.sender` preferred over `tx.origin` in Solidity
I am new to solidity but I have read at many times that tx.origin
should be avoided & msg.sender
should be used.我是刚接触 Solidity 的新手,但我多次读过应该避免使用
tx.origin
并且应该使用msg.sender
。 There is a given demo on this in solidity page .在solidity page中有一个给定的演示。 It says like:-
它说: -
Never use tx.origin for authorization.
切勿使用 tx.origin 进行授权。 Let's say you have a wallet contract like this:
假设你有一个这样的钱包合约:
// 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
}
}
Now someone tricks you into sending Ether to the address of this attack wallet:
现在有人欺骗你将以太币发送到这个攻击钱包的地址:
// 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**
}
}
Now, I want to know that how line 1 will drain entire funds from TxUserWallet
.现在,我想知道第 1 行将如何从
TxUserWallet
耗尽全部资金。 I think transfer()
is a global variable which will just transfer the amount to address in dest
which is of attack wallet.我认为
transfer()
是一个全局变量,它只会将金额转移到攻击钱包的dest
中的地址。 How .transfer()
will trigger receive()
function of contract TxAttackWallet
. .transfer()
将如何触发合约TxAttackWallet
的receive()
function。 Secondly, In line TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
其次,在行
TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
, why are we writing it as TxUserWallet(msg.sender)
, like why are we adding (msg.sender) after contract name and also what values is being passed by writing msg.sender.balance
? ,为什么我们把它写成
TxUserWallet(msg.sender)
,就像我们为什么在合约名称之后添加 (msg.sender) 以及通过写msg.sender.balance
传递什么值?
you should avoid using tx.origin
to prevent the Reentrancy attack.你应该避免使用
tx.origin
来防止重入攻击。 solidity re-entrancy attack explanation solidity 重入攻击解释
Basically, the attacker is withdrawing money from the contract recursively till the balance is 0. So there is a chain of function calls.基本上,攻击者递归地从合约中取钱,直到余额为 0。所以有一个 function 调用链。 A reentrancy attack involves two smart contracts.
重入攻击涉及两个智能合约。 A vulnerable contract and an untrusted attacker's contract.
易受攻击的合约和不受信任的攻击者的合约。 (
fallback
function is called under multiple conditions and this makes writing code for each condition hard. That is why receive
is added. receive
is called only contract receives ether so that fallback should be executed only if a function signature is not implemented within a contract.) (
fallback
function 在多个条件下被调用,这使得为每个条件编写代码变得困难。这就是添加receive
的原因。 receive
仅被称为合约接收以太,因此只有在合约中未实现 function 签名时才应执行后备。 )
msg.sender
is the last caller while tx.origin
is the original caller of those recursive function chain. msg.sender
是最后一个调用者,而tx.origin
是那些递归 function 链的原始调用者。
funcA => funcB => funcC函数A => 函数B => 函数C
msg.sender
is the caller of funcC
whereas tx.origin
is the caller of funcA
msg.sender
是funcC
的调用者,而tx.origin
是funcA
的调用者
Now to prevent the Reentrancy attack we have to make sure that there is no chain of function calls.现在为了防止重入攻击,我们必须确保没有 function 调用链。
require(tx.origin == msg.sender)
In your question, story is a little different.在你的问题中,故事有点不同。 Imagine the workflow.
想象一下工作流程。 You are saying
你是说
Now someone tricks you into sending Ether to the address of this attack wallet:
现在有人欺骗你将以太币发送到这个攻击钱包的地址:
You are sending money to TxAttackWallet
, so you are initiating the function chains.您正在向
TxAttackWallet
,因此您正在启动 function 链。 tx.origin
is you, the owner, tx.origin == owner
. tx.origin
是您,所有者, tx.origin == owner
。 Once you send money to TxAttackWallet
, automatically receive
of TxAttackWallet
will be called so this code will be executed recursively.一旦您向
TxAttackWallet
汇款,将调用TxAttackWallet
的自动receive
,因此此代码将递归执行。 You are in a loop你在一个循环中
TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
You keep transferring funds from your contract to TxUserWallet
.你不断将资金从你的合约转移到
TxUserWallet
。
Who is the tx.origin
?谁是
tx.origin
? It is you because you started this chain of functions so this will always pass:是你,因为你启动了这个函数链,所以它总是会通过:
require(tx.origin == owner);
If you were using the msg.sender==owner
, msg.sender
would be the TxUserWallet
so require condition would not pass and no funds would be transferred.如果您使用的是
msg.sender==owner
, msg.sender
将是TxUserWallet
因此要求条件不会通过并且不会转移任何资金。
How
.transfer()
will triggerreceive()
function of contractTxAttackWallet
.transfer()
将如何触发合约TxAttackWallet
的receive()
function
The receive()
function is invoked automatically when you're sending ETH to the contract address without specifying the data
field.当您在未指定
data
字段的情况下向合约地址发送 ETH 时,会自动调用receive()
function。
It is not possible to specify the data field with the .transfer()
function, you'd need to use the low-level .call()
for that.无法使用
.transfer()
function 指定数据字段,您需要为此使用低级 .call .call()
。
Docs:文件:
receive()
- https://docs.soliditylang.org/en/v0.8.16/contracts.html#receive-ether-function receive()
- https://docs.soliditylang.org/en/v0.8.16/contracts.html#receive-ether-function.transfer()
and .call()
- https://docs.soliditylang.org/en/v0.8.16/types.html#members-of-addresses .transfer()
和 .call .call()
- https://docs.soliditylang.org/en/v0.8.16/types.html#members-of-addresseswhy are we writing it as
TxUserWallet(msg.sender)
, like why are we adding (msg.sender) after contract name and also what values is being passed by writingmsg.sender.balance
?为什么我们将它写为
TxUserWallet(msg.sender)
,就像我们为什么在合约名称之后添加 (msg.sender) 以及通过编写msg.sender.balance
传递的值是什么?
TxUserWallet(msg.sender)
is a contract type pointer to the msg.sender
address (in your case the TxUserWallet
contract address), expecting it to implement all public
and external
functions of TxUserWallet
. TxUserWallet(msg.sender)
是指向msg.sender
地址(在您的情况下为TxUserWallet
合约地址)的合约类型指针,期望它实现TxUserWallet
的所有public
和external
功能。
This way, the TxAttackWallet
contract expects to be able to call the transferTo()
function on msg.sender
and expects to receive empty response, as the transferTo()
function doesn't return any params.这样,
TxAttackWallet
合约期望能够在 msg.sender 上调用transferTo()
msg.sender
并期望收到空响应,因为transferTo()
function 不返回任何参数。
msg.sender.balance
returns the native balance (ETH on Ethereum, BNB on Binance Smart Chain, ...) of msg.sender
, in your case that's the TxUserWallet
contract address. msg.sender.balance
返回 msg.sender 的本机余额(以太坊上的 ETH,Binance 智能链上的 BNB,...),在您的情况下TxUserWallet
msg.sender
地址。
Docs:文件:
TxUserWallet(msg.sender)
- https://docs.soliditylang.org/en/v0.8.16/types.html#contract-typesTxUserWallet(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-types .balance
- https://docs.soliditylang.org/en/v0.8.16/units-and-global-variables.html#members-of-address-types
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.