Ataque Reentrancy explicado: prevenir en Solidity

Article author

Los ataques de reentrancia siguen siendo una de las amenazas más conocidas y persistentes para la seguridad de los smart contracts en Ethereum y cadenas compatibles con EVM. A pesar de años de esfuerzos en la industria, nuevos protocolos continúan enfrentando vulnerabilidades de reentrancia, lo que conduce a pérdidas financieras significativas, a veces alcanzando millones de dólares. Estos exploits suelen aparecer en contratos DeFi de préstamos, staking y puentes donde son comunes las llamadas externas y las transferencias de tokens.

En este artículo, analizamos la mecánica de un ataque de reentrancia, identificamos patrones típicos de contratos vulnerables y compartimos estrategias de mitigación probadas. Basándonos en nuestro análisis de más de 282 auditorías de smart contracts en Soken, destacamos cómo prácticas matizadas de codificación en Solidity y flujos de trabajo de pruebas integrales pueden frustrar las amenazas de reentrancia. Este texto también enlaza riesgos relacionados como el mal uso de delegatecall y tácticas de flash loans para ofrecer una comprensión completa esencial para cualquier desarrollador o auditor de seguridad serio en Web3.


¿Qué es un ataque de reentrancia y cómo explota los smart contracts?

Un ataque de reentrancia ocurre cuando un contrato malicioso llama repetidamente a la función de un contrato vulnerable antes de que la ejecución inicial se complete, explotando el estado inconsistente del contrato.

En la práctica, una vulnerabilidad de reentrancia surge por una mala ordenación de los cambios de estado y las llamadas externas dentro de un contrato. Cuando el contrato transfiere ETH o tokens mediante una llamada o transferencia a una dirección no confiable, ese destinatario puede reentrar recursivamente a la función vulnerable antes de que las variables de estado se actualicen. Esto permite al atacante drenar fondos o manipular la lógica del contrato de forma inesperada.

Por ejemplo, el infame hackeo del DAO en 2016 resultó en un exploit de casi 60 millones de dólares a través de fallas de reentrancia — una historia aleccionadora. Desde entonces, la industria ha identificado que la causa raíz es que el balance o estado del contrato se actualiza sólo después de las llamadas externas, creando una ventana de estado inconsistente que el atacante explota.

Perspectiva experta de las auditorías de Soken: “En más del 40% de nuestras auditorías recientes de seguridad DeFi, aparecen patrones de reentrancia en alguna forma, a menudo variaciones sutiles que involucran contratos proxy o llamadas en capas. Identificar estos requiere un análisis exhaustivo de llamadas inter-contratos más allá de la inspección de un único contrato.”


Patrones comunes en Solidity que causan vulnerabilidades de reentrancia

Las vulnerabilidades de reentrancia suelen originarse por una secuencia específica de operaciones: llamadas externas (como transferencias de ETH o tokens) que ocurren antes de que el contrato actualice sus variables internas de estado.

El patrón más vulnerable se ve así:

function withdraw(uint256 amount) public {
    require(balances[msg.sender] >= amount);
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success);
    balances[msg.sender] -= amount;
}

Aquí la llamada externa usando call.value() ocurre antes de deducir el balance del usuario. Durante esa llamada externa, la función fallback del atacante puede invocar recursivamente withdraw(), drenando fondos antes de que los balances se actualicen.

Constructos clave de riesgo incluyen:

  • Llamadas externas antes de las actualizaciones de estado: Enviar ETH o tokens antes de actualizar balances.
  • Uso de call o send sin precauciones: La llamada externa mediante funciones de bajo nivel ignora chequeos y puede invocar fallback functions.
  • Falta de guardias de reentrancia: Ausencia de mutexes u otros mecanismos de bloqueo.
  • Cadenas de llamadas complejas con delegatecall: Pueden abrir indirectamente puertas a reentrancia a través de llamadas externas dentro de contratos llamados.

En contraste, el patrón más seguro invierte el orden:

function withdraw(uint256 amount) public {
    require(balances[msg.sender] >= amount);
    balances[msg.sender] -= amount;  // actualización de estado primero
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success);
}

Cómo prevenir ataques de reentrancia en Solidity: mejores prácticas y herramientas

La defensa más efectiva contra la reentrancia es actualizar el estado antes de las llamadas externas, combinado con patrones explícitos de protección contra reentrancia.

Técnicas estándar de mitigación:

  1. Patrón Checks-Effects-Interactions:
    Actualice siempre el estado interno antes de realizar llamadas externas. Esto minimiza el estado inconsistente durante las llamadas externas.

  2. Guardias de reentrancia / Mutexes:
    ReentrancyGuard de OpenZeppelin usa un mutex simple para bloquear llamadas anidadas. Incorporarlo así:

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract SecureContract is ReentrancyGuard {
    mapping(address => uint256) balances;

    function withdraw(uint256 amount) public nonReentrant {
        require(balances[msg.sender] >= amount);
        balances[msg.sender] -= amount;
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);
    }
}
  1. Limitar llamadas externas:
    Minimice las transferencias o interacciones dentro de funciones críticas—use pagos pull en lugar de push donde los usuarios retiran fondos explícitamente.

  2. Análisis estático y herramientas automáticas:
    Herramientas como Slither y los propios frameworks de auditoría de Soken detectan reentrancia.

  3. Usar las últimas versiones y características de Solidity:
    Solidity 0.8+ introduce chequeos incorporados de overflow, pero los riesgos de reentrancia persisten, por lo que se debe combinar la seguridad del código con patrones de diseño.

Metodología Soken: Nuestras auditorías integran modelado manual de amenazas, ejecución simbólica y fuzz testing, que detectan flujos complejos de reentrancia que a menudo pasan desapercibidos por escáneres genéricos.


Ejemplos reales de exploits por reentrancia y su impacto

Los ataques de reentrancia siguen siendo frecuentes a pesar del aumento de conciencia. A continuación, una comparación de hacks notables que ilustran la diversidad y costo de estos exploits:

Incidente Fecha Monto de pérdida Patrón explotado Lección clave
The DAO Hack 2016-06 ~$60 millones Reentrancia en propuesta splitDAO Actualizar estado antes de la llamada externa
The Lendf.Me Hack 2020-04 $25 millones+ Reentrancia amplificada por flash loan Combinar guardia de reentrancia + resistencia a flash loans
Euler Finance Hack 2023-03 $197 millones Reentrancia anidada + delegatecall Cadenas complejas de llamadas incrementan riesgo
Hack reciente en puente DeFi 2025-11 $15 millones Exploit de reentrancia en transferencia de tokens Auditar interacciones entre contratos

Nota: Las auditorías de Soken enfatizan la seguridad en capas, especialmente alrededor de llamadas inter-contratos y estándares de tokens que pueden disparar funciones fallback dinámicamente.


Delegatecall y vectores de flash loan: amplificando riesgos de reentrancia

Delegatecall, que ejecuta código en el contexto del contrato llamante, puede oscurecer el riesgo de reentrancia y aumentar la superficie de ataque, especialmente combinado con flash loans.

Los ataques de reentrancia a menudo aprovechan los flash loans para maximizar capital para drenajes recursivos:

  • Los flash loans proveen liquidez inmediata grande en una sola transacción.
  • Un atacante usa fondos prestados vía flash loan para disparar llamadas reentrantes y drenar la liquidez del protocolo antes de reembolsar el préstamo.
  • El uso de delegatecall puede desencadenar código externo no confiable que altera el estado del contrato o permite reentradas.

Insight de seguridad: “Según la experiencia de Soken, la mitigación de reentrancia va más allá de contratos individuales al diseño a nivel de protocolo — especialmente cuando delegatecall y flash loans coexisten. Los contratos deben combinar guardias de reentrancia con validación de entrada y chequeos específicos para flash loans (topes en montos, límites a llamadas recursivas).”


Tabla comparativa: técnicas comunes para prevenir reentrancia

Técnica Descripción Pros Contras Uso
Checks-Effects-Interactions Actualiza estado antes de llamar externo Simple, efectiva Requiere disciplina Práctica base de seguridad
Reentrancy Guard (mutex) Usa modifiers de Solidity para evitar reentradas Bloquea llamadas recursivas explícitamente Añade ligera sobrecarga de gas Recomendado para todas las funciones de retiro
Patrón Pull Payment Permite a usuarios retirar fondos voluntariamente Minimiza riesgos de transferencias automáticas Requiere interacción del usuario Ideal para contratos de escrow y staking
Análisis estático y dinámico Auditorías automáticas y manuales Detecta vulnerabilidades temprano Puede pasar por alto flujos complejos entre contratos Esencial para aseguramiento pre-despliegue
Chequeos Flash Loan & Delegatecall Validar contexto del caller y transacción Previene reentrancia amplificada por préstamo Complejo de implementar Crítico en protocolos DeFi con préstamos

Ejemplo de código Solidity: contrato vulnerable a reentrancia y su corrección

Aquí un contrato mínimo vulnerable y su versión endurecida usando un guardia de reentrancia:

// Vulnerable: retiro antes de actualización de balance
contract Vulnerable {
    mapping(address => uint256) public balances;

    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) external {
        require(balances[msg.sender] >= amount, "Insufficient funds");
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
        balances[msg.sender] -= amount; // Vulnerable: estado actualizado luego de llamada externa
    }
}

Corregido con ReentrancyGuard de OpenZeppelin y efectos antes de interacción:

import "@openzeppelin/contracts/security/ReentrancyGuard.sol";

contract Secure is ReentrancyGuard {
    mapping(address => uint256) public balances;

    function deposit() external payable {
        balances[msg.sender] += msg.value;
    }

    function withdraw(uint256 amount) external nonReentrant {
        require(balances[msg.sender] >= amount, "Insufficient funds");
        balances[msg.sender] -= amount; // Estado actualizado antes de llamada externa
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}

Consejo profesional: Combine siempre el patrón checks-effects-interactions con guardias de reentrancia. Auditar grafos de llamadas de contratos y probar con fuzzing puede revelar vulnerabilidades sutiles de reentrancia multi-hop antes del despliegue.


Las vulnerabilidades de reentrancia siguen siendo una amenaza crítica para los smart contracts, especialmente en ecosistemas DeFi complejos que usan flash loans y delegatecall. Las defensas más efectivas combinan disciplina de código (checks-effects-interactions), guardias explícitas de reentrancia, auditorías rigurosas y validación contextual de transacciones.

En Soken, nuestra investigación en seguridad integra técnicas manuales y automáticas perfeccionadas a partir de 282+ auditorías de smart contracts, identificando y remediando consistentemente reentrancia y riesgos asociados. Aprovechar mejores prácticas y metodologías de prueba exhaustivas es imperativo para todo proyecto que maneje fondos de usuarios.


¿Necesita guía experta en seguridad? El equipo de auditores de Soken ha revisado más de 255 smart contracts y asegurado más de $2B en valor de protocolo. Ya sea que necesite una auditoría integral, una evaluación gratuita de seguridad X-Ray o ayuda para navegar regulaciones cripto, estamos listos para ayudarle.

Hable con un experto de Soken | Vea nuestros informes de auditoría

Article author

Preguntas frecuentes

¿Qué es un ataque de reentrancy en smart contracts?

Un ataque de reentrancy ocurre cuando un contrato malicioso llama repetidamente al contrato víctima antes de que la primera llamada termine, explotando llamadas externas para drenar activos o alterar el estado inesperadamente.

¿Qué patrones de smart contracts son más vulnerables al reentrancy?

Los contratos que realizan llamadas externas, como transferencias de tokens o llamadas a otros contratos, antes de actualizar estados internos son altamente vulnerables debido al riesgo de reentradas recursivas durante estas llamadas.

¿Cómo se pueden prevenir las vulnerabilidades de reentrancy en Solidity?

Métodos comunes incluyen usar el patrón Checks-Effects-Interactions, aplicar el modificador 'ReentrancyGuard' de OpenZeppelin y minimizar llamadas externas o utilizar métodos de pago pull sobre push.

¿Qué herramientas ayudan a detectar vulnerabilidades de reentrancy antes del despliegue?

Analizadores automáticos como MythX, Slither y Securify detectan patrones de reentrancy. Auditorías manuales exhaustivas y pruebas fuzz mejoran la identificación de vulnerabilidades.

¿Delegatecall y flash loans están relacionados con ataques de reentrancy?

Sí, el mal uso de delegatecall puede permitir exploits de reentrancy al ejecutar código en el contexto de otro contrato. Los flash loans pueden amplificar el impacto, aunque no son ataques de reentrancy en sí mismos.

Chat