智能合約安全與審計

tx.origin 釣魚攻擊

tx.origin 釣魚攻擊利用一個以 tx.origin、而非 msg.sender 來授權呼叫者的合約。在 EVM 中,msg.sender 是當前函式的直接呼叫者,而 tx.origin 是啟動整條交易鏈的外部帳戶。當 A 呼叫合約 B、B 又呼叫合約 C,在 C 內 msg.sender 是 B,但整條鏈的 tx.origin 始終是 A。一個檢查 require(tx.origin == owner) 的合約,因此問錯了問題:它確認的是誰啟動了交易,而非誰實際在呼叫它。

這道縫隙正是釣魚攻擊者所利用的。攻擊者部署一個惡意合約,誘使受害者(擁有者)去呼叫它,或許偽裝成空投領取或無害的互動。這個惡意合約如今以擁有者為 tx.origin 在運行,於是去呼叫脆弱目標的特權函式。目標檢查 tx.origin、看到擁有者、便放行,攻擊者於是掏空資金或奪取控制權,儘管擁有者根本無意呼叫目標。擁有者只需被誘騙進行一筆看似無辜的交易即可。

修法簡單而絕對:用 msg.sender 授權、絕不用 tx.origin,如此權限便無法透過中介合約被借用。tx.origin 在歷史上唯一正當的用途——以 require(tx.origin == msg.sender) 確保呼叫者是純帳戶而非合約——本身也正在式微。帳戶抽象化把一般使用者變成智能合約帳戶,於是那道檢查如今會錯誤地把正當使用者擋在外面,而且也提供不了真正的安全好處。請把 tx.origin 當成幾乎從來都不是授權的正確工具。

// VULNERABLE
function transfer(address to, uint256 amt) external {
    require(tx.origin == owner); // wrong: phishable
    _send(to, amt);
}

// SAFE
function transfer(address to, uint256 amt) external {
    require(msg.sender == owner); // immediate caller
    _send(to, amt);
}

授權直接呼叫者,而非交易的發起者。

授權請用 msg.sender,絕不用 tx.origin。用 tx.origin == msg.sender 來擋合約呼叫者的老把戲也正在凋零:帳戶抽象化讓正當使用者成為合約,於是它如今把他們擋在外面,卻沒添加任何真正的安全。

又稱
tx.origin authorization bugtx.origin 授權漏洞