tx.origin
es una variable global en Solidity que devuelve la dirección de la cuenta que envió la transacción. Los contratos que utilizan tx.origin
para autorizar a los usuarios son vulnerables a los ataques de phishing.
¿Cómo?
Digamos que se podría hacer una llamada al contrato vulnerable que pasa la verificación de autorización ya que tx.origin
devuelve el remitente original de la transacción que en este caso es la cuenta autorizada.
Veamos el ejemplo.
contract Wallet { address public owner; constructor() payable { owner = msg.sender; } function transfer(address payable _to, uint _amount) public { require(tx.origin == owner); (bool sent, ) = _to.call{value: _amount}(""); require(sent, "Failed to send Ether"); } } contract Attack { address payable public owner; Wallet wallet; constructor(Wallet _wallet) { wallet = Wallet(_wallet); owner = payable(msg.sender); } function attack() public { wallet.transfer(owner, address(wallet).balance); } }
Creé dos contratos: Wallet
que almacena y retira fondos, y Attack
, que es un contrato realizado por un atacante que quiere atacar el primer contrato.
Tenga en cuenta que el contrato autoriza la función de transfer
utilizando tx.origin
.
Ahora, si el propietario del contrato de Wallet
envía una transacción con suficiente gas a la dirección de Attack
, invocará la función de respaldo, que a su vez llama a la función de transfer
del contrato de Wallet
con el atacante de parámetros.
Como resultado, todos los fondos del contrato de Wallet
se retirarán a la dirección del atacante. Esto se debe a que la dirección que primero inició la llamada era la de la víctima (es decir, el propietario del contrato de Wallet
).
Por lo tanto, tx.origin
será igual al propietario y se pasará el require
.
La mejor manera de prevenir los ataques de Tx Origin es no utilizar tx.origin
con fines de autenticación. En su lugar, es recomendable utilizar msg.sender
(ver más abajo)
function transfer(address payable _to, uint256 _amount) public { require(msg.sender == owner); (bool sent, ) = _to.call.value(_amount)(""); require(sent, "Failed to send Ether"); }
También publicado aquí .