[英]`msg.sender` preferred over `tx.origin` in Solidity
我是剛接觸 Solidity 的新手,但我多次讀過應該避免使用tx.origin
並且應該使用msg.sender
。 在solidity page中有一個給定的演示。 它說: -
切勿使用 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()
function。 其次,在行TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
,為什么我們把它寫成TxUserWallet(msg.sender)
,就像我們為什么在合約名稱之后添加 (msg.sender) 以及通過寫msg.sender.balance
傳遞什么值?
你應該避免使用tx.origin
來防止重入攻擊。 solidity 重入攻擊解釋
基本上,攻擊者遞歸地從合約中取錢,直到余額為 0。所以有一個 function 調用鏈。 重入攻擊涉及兩個智能合約。 易受攻擊的合約和不受信任的攻擊者的合約。 ( fallback
function 在多個條件下被調用,這使得為每個條件編寫代碼變得困難。這就是添加receive
的原因。 receive
僅被稱為合約接收以太,因此只有在合約中未實現 function 簽名時才應執行后備。 )
msg.sender
是最后一個調用者,而tx.origin
是那些遞歸 function 鏈的原始調用者。
函數A => 函數B => 函數C
msg.sender
是funcC
的調用者,而tx.origin
是funcA
的調用者
現在為了防止重入攻擊,我們必須確保沒有 function 調用鏈。
require(tx.origin == msg.sender)
在你的問題中,故事有點不同。 想象一下工作流程。 你是說
現在有人欺騙你將以太幣發送到這個攻擊錢包的地址:
您正在向TxAttackWallet
,因此您正在啟動 function 鏈。 tx.origin
是您,所有者, tx.origin == owner
。 一旦您向TxAttackWallet
匯款,將調用TxAttackWallet
的自動receive
,因此此代碼將遞歸執行。 你在一個循環中
TxUserWallet(msg.sender).transferTo(owner, msg.sender.balance);
你不斷將資金從你的合約轉移到TxUserWallet
。
誰是tx.origin
? 是你,因為你啟動了這個函數鏈,所以它總是會通過:
require(tx.origin == owner);
如果您使用的是msg.sender==owner
, msg.sender
將是TxUserWallet
因此要求條件不會通過並且不會轉移任何資金。
.transfer()
將如何觸發合約TxAttackWallet
的receive()
function
當您在未指定data
字段的情況下向合約地址發送 ETH 時,會自動調用receive()
function。
無法使用.transfer()
function 指定數據字段,您需要為此使用低級 .call .call()
。
文件:
receive()
- https://docs.soliditylang.org/en/v0.8.16/contracts.html#receive-ether-function.transfer()
和 .call .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()
msg.sender
並期望收到空響應,因為transferTo()
function 不返回任何參數。
msg.sender.balance
返回 msg.sender 的本機余額(以太坊上的 ETH,Binance 智能鏈上的 BNB,...),在您的情況下TxUserWallet
msg.sender
地址。
文件:
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-types
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.