簡體   English   中英

在 Solidity 中,`msg.sender` 優於 `tx.origin`

[英]`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()將如何觸發合約TxAttackWalletreceive() 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.senderfuncC的調用者,而tx.originfuncA的調用者

現在為了防止重入攻擊,我們必須確保沒有 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==ownermsg.sender將是TxUserWallet因此要求條件不會通過並且不會轉移任何資金。

.transfer()將如何觸發合約TxAttackWalletreceive() function

當您在未指定data字段的情況下向合約地址發送 ETH 時,會自動調用receive() function。

無法使用.transfer() function 指定數據字段,您需要為此使用低級 .call .call()

文件:


為什么我們將它寫為TxUserWallet(msg.sender) ,就像我們為什么在合約名稱之后添加 (msg.sender) 以及通過編寫msg.sender.balance傳遞的值是什么?

TxUserWallet(msg.sender)是指向msg.sender地址(在您的情況下為TxUserWallet合約地址)的合約類型指針,期望它實現TxUserWallet的所有publicexternal功能。

這樣, TxAttackWallet合約期望能夠在 msg.sender 上調用transferTo() msg.sender並期望收到空響應,因為transferTo() function 不返回任何參數。

msg.sender.balance返回 msg.sender 的本機余額(以太坊上的 ETH,Binance 智能鏈上的 BNB,...),在您的情況下TxUserWallet msg.sender地址。

文件:

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM