⛓️ Blockchain Learning Path GitHub

09 · Seguridad y auditoría

Nivel: Avanzado · ⏱️ Duración estimada: 180 min · Fuente: Trail of Bits Building Secure Contracts y ConsenSys Smart Contract Best Practices ⬅️ Currículo · 📚 Bibliografía 🧭 ⬅️ Anterior: 08 · Tokens y estándares · 📚 Índice · ➡️ Siguiente: 10 · Oráculos, almacenamiento e indexación 📖 Glosario de términos · 🌱 ¿Nuevo en esto? Empieza aquí


🎯 Objetivos

📚 Resultados de aprendizaje

Al finalizar, el estudiante podrá:

  1. Identificar reentrancia, fallos de control de acceso, lógica económica y manipulación de oráculo en código real.
  2. Ejecutar un ciclo de auditoría con alcance, actores, activos e invariantes documentados.
  3. Construir pruebas de fuzzing e invariantes que expongan una vulnerabilidad concreta.
  4. Reproducir un exploit con una prueba mínima y explicar la corrección que lo neutraliza.
  5. Clasificar hallazgos por impacto y probabilidad para priorizar su remediación.
  6. Aplicar herramientas de análisis estático como Slither dentro del flujo de revisión.

🗺️ Temas

# Tema Por qué importa
1 Reentrancia Permite reentrar antes de actualizar estado y drenar fondos; patrón clásico y aún vigente.
2 Control de acceso Un onlyOwner faltante o mal aplicado entrega el contrato a cualquiera.
3 Lógica económica Errores de precisión, redondeo y flujos de valor generan pérdidas silenciosas.
4 Front-running y MEV El orden de las transacciones es manipulable y afecta a subastas y liquidaciones.
5 Manipulación de oráculo Un precio spot barato de mover habilita ataques, a veces vía flash loan.
6 delegatecall y upgrades Ejecutar código externo sobre el propio almacenamiento puede corromper el estado.
7 DoS y agotamiento de gas Bucles no acotados o dependencias externas pueden bloquear funciones críticas.
8 Firmas y replay Firmas sin nonce o sin chainId se reutilizan en otra cadena o transacción.

🧠 Modelo mental

Auditar un contrato se parece a inspeccionar un edificio antes de abrirlo al público: no basta con que las luces enciendan. Recorres cada puerta preguntando quién puede abrirla, qué pasa si alguien fuerza el orden de entrada y salida, y qué ocurre si un proveedor externo, como el ascensor, deja de responder. El código "funciona" en la ruta feliz; el auditor busca las rutas que nadie quiso recorrer.

El límite de la analogía: un edificio es estático y su inspector confía en normas maduras, mientras que un contrato opera en un entorno adversarial y componible donde otros contratos pueden llamarlo en cualquier orden y un atacante puede pedir prestado capital enorme por unos segundos. Por eso la seguridad no termina en una lista de comprobación: exige razonar sobre invariantes que deben cumplirse pase lo que pase.

🧩 Esquema visual

La reentrancia clásica explota que el contrato envía valor antes de actualizar su estado: el fallback del atacante reingresa mientras el saldo sigue intacto.

sequenceDiagram
    participant A as "Atacante"
    participant V as "Vault"
    A->>V: withdraw
    V->>A: call externo que envia ETH
    Note over A: se ejecuta el fallback del atacante
    A->>V: withdraw de nuevo
    V->>A: call externo que envia ETH otra vez
    Note over V: el saldo aun no se habia actualizado
    V->>V: actualiza el saldo demasiado tarde

Una auditoría profesional es un ciclo con verificación final, no una lectura única del código.

flowchart LR
    A["Definir alcance e invariantes"] --> B["Revisión manual del código"]
    B --> C["Pruebas, fuzzing e invariantes"]
    C --> D["Hallazgos clasificados por severidad"]
    D --> E["Corrección por el equipo"]
    E --> F["Verificación del fix"]
    F --> B

📖 Conceptos y definiciones

🔬 Profundización

Severidad: la matriz impacto × probabilidad

Las firmas de auditoría no reportan "bugs" sueltos: clasifican cada hallazgo cruzando cuánto daño causaría (fondos perdidos, protocolo congelado, dato incorrecto) con qué tan plausible es que ocurra (¿lo dispara cualquiera, o exige condiciones improbables y capital enorme?). Una matriz típica:

Impacto \ Probabilidad Alta Media Baja
Alto (pérdida de fondos) Crítica Alta Media
Medio (funcionalidad degradada) Alta Media Baja
Bajo (molestia o gas extra) Media Baja Informativa

Trail of Bits, OpenZeppelin y las plataformas de concursos como Code4rena o Sherlock usan variantes de este esquema; lo importante no es la etiqueta exacta sino que la clasificación sea argumentada y reproducible. Un hallazgo "crítico pero teórico" mal justificado destruye la credibilidad de un informe tanto como uno real que se pasó por alto.

Mini-casos históricos: la misma lección, distinta década

Caso Año Pérdida histórica Causa raíz Lección
The DAO 2016 ~3.6 M ETH Reentrancia: enviaba ETH antes de actualizar el saldo Checks-effects-interactions no es opcional; el incidente motivó el fork que separó Ethereum de Ethereum Classic
Ronin Bridge 2022 ~624 M USD Claves de 5 de 9 validadores comprometidas vía ingeniería social La criptografía perfecta no protege un quorum de confianza demasiado pequeño y mal custodiado
Wormhole 2022 ~326 M USD Verificación de firma defectuosa: aceptaba una función de validación obsoleta Todo lo que "verifica" debe probarse con entradas hostiles, no solo con las válidas
Euler Finance 2023 ~197 M USD Una función de donación rompía el invariante de solvencia usado por la liquidación Cada función nueva debe evaluarse contra los invariantes de todo el sistema; los fondos fueron devueltos tras negociación

Nótese el patrón: solo uno de los cuatro es "un bug de Solidity" clásico. Los otros tres son fallos de diseño, de custodia de claves o de interacción entre módulos correctos por separado.

Qué detecta cada técnica (y qué no)

Ninguna herramienta cubre todo el espectro; una auditoría seria las apila porque sus puntos ciegos son complementarios:

Clase de bug Análisis estático (Slither) Fuzzing (Foundry/Echidna) Invariantes Revisión manual
Reentrancia por patrón de código ✅ detecta el patrón ⚠️ solo si la prueba lo ejercita ✅ si el invariante de saldos está definido
Control de acceso faltante ✅ parcial ✅ con llamadas desde cuentas aleatorias
Redondeo y precisión ✅ excelente con entradas extremas ⚠️ fácil de pasar por alto
Lógica económica multi-contrato ⚠️ requiere entorno completo ✅ la técnica más fuerte
Error de diseño del protocolo ✅ única técnica que lo ve

La consecuencia práctica: un slither . limpio y un fuzzing en verde acotan clases enteras de errores, pero el caso Euler demuestra que el fallo puede vivir en la interacción entre funciones individualmente correctas, territorio exclusivo de los invariantes bien elegidos y de la revisión humana.

Anatomía de un ataque de préstamo relámpago

Los retos de este módulo se entienden mejor viendo cómo se combinan. Un flash loan no es una vulnerabilidad: es una herramienta que elimina el capital como barrera de entrada. Quien no tiene un millón puede operar como si lo tuviera, siempre que lo devuelva en la misma transacción.

Eso convierte ataques que eran teóricos en ataques que cualquiera puede ejecutar.

El ataque completo, en una sola transacción atómica:

1. Pedir prestados 10 000 000 USDC          ← sin colateral: se devuelve al final o revierte todo
2. Comprar TOKEN en un pool pequeño         ← el precio spot de ese pool se dispara
3. Depositar TOKEN como garantía en el      ← el protocolo lee el precio del pool manipulado
   protocolo víctima, que lo valora caro       y concede un préstamo desproporcionado
4. Retirar el préstamo en USDC
5. Deshacer la compra: el precio vuelve
6. Devolver los 10 000 000 + comisión
7. Quedarse la diferencia

Lo que hay que ver: el atacante no rompió ninguna criptografía ni encontró un desbordamiento. Usó cada contrato exactamente como estaba escrito. El fallo está en un supuesto: que el precio de un pool refleja el valor de mercado. Es cierto en condiciones normales y falso durante un bloque con liquidez prestada.

Por qué la atomicidad lo hace posible: si algo sale mal en cualquier paso, toda la transacción revierte y el préstamo nunca existió. El atacante no arriesga capital, solo gas. Un intento fallido cuesta unos céntimos.

Las tres defensas, por orden de eficacia:

Defensa Qué logra Límite
TWAP en lugar de spot Manipular la media exige sostener el precio muchos bloques, lo que multiplica el coste Reacciona con retraso: en un movimiento real de mercado, va por detrás
Agregar varias fuentes Un solo pool manipulado no mueve la mediana Añade dependencia de más proveedores, cada uno con su riesgo
Circuit breaker Detiene la operación si el precio se sale de rango Alguien tiene que definir "rango razonable", y ese alguien es un punto de confianza

💡 En una frase: los ataques caros de verdad no rompen el código, rompen un supuesto. Escribe los supuestos de tu contrato antes de escribir el contrato.

🎓 Si ya dominas esto — método de auditoría, más allá del catálogo de bugs
  • Empieza por las invariantes, no por el código. "La suma de saldos iguala el suministro", "nadie retira más de lo que depositó". Un catálogo de vulnerabilidades encuentra lo conocido; una invariante bien escrita encuentra lo que nadie ha catalogado todavía.
  • La reentrancia entre funciones distintas evade el guard ingenuo. nonReentrant en retirar() no protege si el reingreso ocurre por transferir(). Hay que razonar sobre el estado compartido, no sobre la función.
  • La reentrancia de solo lectura fue la sorpresa de 2022. Una función view consultada a mitad de una transferencia devuelve un estado inconsistente; un tercero que confía en ese view toma decisiones sobre datos que no representan ningún estado real. No hay escritura, y aun así hay agujero.
  • tx.origin no es control de acceso. Distingue quién inició la cadena de llamadas de quién llama directamente; usarlo para autorizar permite que un contrato intermedio actúe en nombre de la víctima. Se usa msg.sender, siempre.
  • La severidad no es una etiqueta, es impacto × probabilidad. Un fallo que drena todo con una condición irrepetible puede ser menos urgente que uno que filtra poco de forma continua. Un informe que no argumenta ambos ejes no ayuda a priorizar.
  • Ninguna herramienta sustituye la revisión manual. Slither y Echidna encuentran patrones y violaciones de propiedades que tú definiste. Que la lógica de negocio permita retirar el doble no es un patrón: es una propiedad que solo detecta quien entendió el negocio.

🧪 Laboratorio guiado

  1. Abre los retos del repositorio en security-challenges y compila los contratos para partir de una base limpia.
forge build
  1. Ejecuta las pruebas con trazas para observar el flujo de un exploit reproducido paso a paso.
forge test -vv
  1. Pasa el análisis estático sobre los contratos y contrasta cada alerta de slither con una revisión manual del flujo señalado.

  2. Consulta el catálogo de laboratorios para elegir el reto adecuado a la vulnerabilidad que estudias.

📝 Reto verificable

Toma un contrato vulnerable de security-challenges, escribe una prueba mínima que reproduzca el exploit, corrige la causa raíz y documenta el hallazgo con su clasificación de impacto y probabilidad.

Criterio de aceptación: existe una prueba que falla contra el contrato vulnerable y pasa contra el corregido; el informe describe alcance, activos, invariante violado y la corrección; y forge test -vv queda en verde tras el arreglo sin desactivar la prueba del exploit.

⚠️ Errores frecuentes

Síntoma Causa y cómo comprobarlo
Fondos drenados en una sola transacción Reentrancia; busca llamadas externas antes de actualizar estado y añade una prueba de reingreso.
Cualquiera ejecuta una función privilegiada Falta o mal uso de modificadores de acceso; prueba llamando desde una cuenta sin rol.
El precio usado es absurdo por un instante Oráculo spot manipulable; valida antigüedad, rango y considera TWAP.
Un proxy queda inutilizable tras actualizar Colisión de almacenamiento en delegatecall; compara los layouts antes y después.
Una firma vale en otra cadena Falta de chainId/nonce; revisa el dominio EIP-712 y añade protección de replay.
El análisis estático "no encuentra nada" Falsa sensación de seguridad; ninguna herramienta sustituye la revisión manual de invariantes.

🛡️ Seguridad y ética

🔗 Referencias

✅ Criterio de dominio


🧭 Navegación

⬅️ Módulo 08 · Tokens y estándares · 📚 Índice del currículo · ➡️ Módulo 10 · Oráculos, almacenamiento e indexación


🧠 Autoevaluación del módulo

Responde sin volver atrás. Cada opción incorrecta corresponde a un error frecuente documentado en este mismo módulo: si fallas, la explicación te dice qué releer.