Seguridad en smart contracts con Slither

Resumen

La seguridad en smart contracts es crítica porque, una vez desplegado el código en la blockchain, no puedes modificarlo ni parchear errores en tiempo de ejecución. Aquí vas a entender cómo proteger tus contratos en Solidity frente a vulnerabilidades comunes, qué patrones aplicar y qué herramientas usar para auditar tu código antes de publicarlo.

¿Por qué la seguridad en Solidity es distinta al software tradicional?

A diferencia del software convencional, en Solidity no tienes la oportunidad de hacer un hotfix. Si descubres una vulnerabilidad después del deploy, esa falla queda viva en la red, y los usuarios y fondos asociados quedan expuestos.

El 99% de los problemas de seguridad provienen de llamadas externas: cuando tu contrato consume otro contrato, hereda parte de su superficie de ataque. Por eso, lo primero es preguntarte qué contratos estás consumiendo y cuánto confías en ellos.

¿Qué es una llamada externa en Solidity? Es cuando tu contrato invoca funciones de otro contrato, por ejemplo para emitir tokens o transferir NFTs. Cada llamada externa amplía la superficie de riesgo y debe tratarse con cuidado.

¿Cómo controlar las llamadas externas de forma segura?

La recomendación práctica es limitar el número de llamadas externas y restringirlas solo a direcciones conocidas, idealmente contratos que tú mismo desplegaste o que puedes verificar en un explorer.

  • Evita permitir que cualquier cuenta modifique las direcciones de los contratos referenciados.
  • Rastrea el flujo de cada llamada externa y aísla su impacto.
  • Diseña el contrato para que, si una referencia externa cae, no se rompa toda la lógica.

Un ejemplo concreto: si el contrato de la currency falla, eso no debería afectar la jugabilidad del Tic Tac Toe, aunque sí podría impactar al marketplace. Por eso el marketplace debe tener medidas de contención independientes.

¿Qué patrón evita ataques de reentrancy en smart contracts?

La regla de oro es: actualiza el estado primero y haz las llamadas externas al final. Esto se conoce como el patrón checks-effects-interactions.

Cuando determinas al ganador en un juego, la emisión de la moneda (que implica una llamada externa) debe ser el último paso. Si primero resuelves todo lo relacionado con almacenamiento y estado local, llegas a la llamada externa con el contrato ya consistente.

¿Qué es un ataque de reentrancy? Es cuando un atacante explota que el estado del contrato aún no se actualizó y vuelve a entrar a la misma función varias veces para drenar fondos o duplicar acciones.

Para reforzar esta defensa, puedes apoyarte en librerías como OpenZeppelin, que ofrece un ReentrancyGuard. Eso sí, ten en cuenta que la transferencia de Ether y la transferencia de tokens siguen rutas distintas, así que las protecciones aplican de forma diferente.

¿Cómo prevenir denegación de servicio en un contrato?

Otro riesgo de las llamadas externas es la denegación de servicio (denial of service). Imagina que alguien logra cambiar la dirección del token a un contrato falso cuya función issue siempre hace revert. A partir de ese momento, esa línea fallaría siempre, y ningún partido podría cerrarse con ganador. El flujo del proyecto quedaría roto.

La solución es separar balances de pagos:

  1. Cuando el usuario gana, no emitas el token en ese momento. Solo actualiza un mapping que registre cuántas monedas tiene a su favor.
  2. Crea una función de retiro independiente, donde el usuario decide cuándo cobrar.
  3. La emisión real de los tokens ocurre solo dentro de esa función de withdraw.

Así, si el retiro falla, lo resuelves después, pero el juego sigue terminando con un ganador y el registro queda intacto.

¿Qué herramienta usar para analizar vulnerabilidades en Solidity?

Una herramienta muy útil es Slither, un analizador estático de código que puedes encontrar en GitHub. Lo instalas siguiendo las instrucciones y, si trabajas en un entorno como Truffle, ya reconoce los comandos y los frameworks más comunes.

Al correrlo, Slither compila los contratos y revisa el código en busca de patrones peligrosos. En un ejemplo simple, la herramienta puede advertirte que la versión 0.8.19 de Solidity todavía es muy reciente para producción, sugiriendo usar versiones más maduras y probadas cuando defines un rango como 0.7 a 0.9.

  • Detecta reentrancy potencial.
  • Identifica versiones de compilador no recomendadas.
  • Señala patrones inseguros en el manejo de estado y llamadas.

Ejecutar este tipo de análisis estático antes del deploy te da una capa extra de certeza de que el código no tiene vulnerabilidades evidentes.

¿Cuáles son los tres pilares de seguridad en smart contracts?

La seguridad sólida en Solidity se apoya en tres pilares que conviene aplicar siempre:

  1. Usar una herramienta de análisis estático como Slither para revisar el código antes de publicarlo.
  2. Conocer y estudiar errores conocidos, como reentrancy y denial of service, para evitarlos al diseñar la lógica.
  3. No confiar solo en tu propio criterio: comparte el código con colegas y, si el caso de uso es sensible, como un escenario DeFi, contrata un servicio de auditoría o consultoría que haga un análisis detallado.

Este último punto es el que más se subestima. Una segunda mirada profesional puede detectar vulnerabilidades que tú, por estar tan inmerso en el código, no logras ver. Y en DeFi, donde se mueven fondos reales, un descuido puede costar muchísimo a los usuarios.

¿Has aplicado alguno de estos patrones en tus contratos? Cuéntame en los comentarios cuál vulnerabilidad te preocupa más resolver en tu proyecto.