Inicio
» Conocimiento
»
Guía paso a paso para auditar el contrato inteligente de un proyecto de criptomonedas.
Guía paso a paso para auditar el contrato inteligente de un proyecto de criptomonedas.
Una auditoría de contratos inteligentes es un proceso estructurado para descubrir cómo un contrato puede fallar, usarse indebidamente o controlarse de forma inesperada para los usuarios. No es lo mismo que ejecutar un escáner, leer una insignia de auditoría o confirmar que el código fuente está verificado. Utilice el siguiente flujo de trabajo para revisar un contrato EVM implementado o un código fuente antes de confiarle fondos importantes.
Importante: Este es un marco de revisión práctico, no una garantía de seguridad para un proyecto ni asesoramiento de inversión. Un protocolo de producción de gran valor debe ser revisado de forma independiente por profesionales de seguridad con experiencia. Las imágenes de interfaz que aparecen en esta guía son ilustrativas y no deben considerarse como evidencia sobre ningún proyecto o implementación en particular.
Lista de verificación de auditoría de un vistazo
Paso
Pregunta principal
Evidencia útil
1. Alcance
¿Estoy revisando el contrato exacto al que llaman los usuarios?
Dirección, cadena, código de bytes, proxy e implementación
2. Puntos de entrada
¿Qué puede hacer cada persona que llama?
Funciones públicas/externas, cambios de estado, gráfico de llamadas
3. Automatización
¿Qué patrones evidentes merecen atención?
Salida del compilador, hallazgos de Slither, clasificación del detector
4. Seguridad manual
¿Puede una secuencia de llamadas romper las suposiciones?
Llamadas externas, reentrada, devoluciones de llamada, manejo de fallos
5. Lógica
¿Sigue siendo correcta la contabilidad en los casos extremos?
Aritmética, redondeo, comisiones, límites, transiciones de estado
6. Privilegios
¿Quién puede cambiar o detener el sistema?
Roles, propietario, claves de administrador, proxy, inicializador
7. Pruebas
¿Se mantiene el comportamiento ante entradas y secuencias inesperadas?
Pruebas unitarias, de fuzzing, de invariantes y de bifurcación
8. Informes
¿Puede otra persona reproducir el resultado y volver a realizar la prueba?
Hallazgo, impacto, evidencia, corrección y estado de la nueva prueba
Paso 1: Confirmar el alcance y el artefacto desplegado.
Una vista de verificación de contrato que muestra las comprobaciones de red, dirección, versión del compilador y coincidencia de código de bytes que se deben registrar antes del análisis.
Comience con la cadena y la dirección exactas. Registre la dirección de despliegue, el hash de la transacción, el número de bloque, la versión del compilador, la configuración del optimizador, los argumentos del constructor y la confirmación o versión que el equipo indica que se desplegó. Un proyecto puede tener varias direcciones para un token, enrutador, bóveda, proxy, implementación, oráculo o despliegue de prueba. Revisar una dirección incorrecta no tiene valor práctico.
Compruebe si el código fuente verificado del explorador reproduce el código de bytes implementado. La verificación es útil porque permite inspeccionar el código fuente y la ABI, pero solo comprueba la identidad: no garantiza la seguridad de la lógica de negocio. Si el contrato es actualizable, identifique tanto el proxy como su implementación actual. Lea la dirección de implementación en la documentación del mecanismo del proxy o en la información del explorador y confirme que se trata de la implementación que desea revisar. La guía oficial de verificación de Foundry de Etherscan documenta la verificación para contratos nuevos y existentes.
Defina también los límites. Incluya las bibliotecas importadas, los contratos heredados, las bibliotecas vinculadas, los contratos auxiliares implementados, los adaptadores de oráculo, los tokens recibidos de los usuarios y los componentes privilegiados fuera de la cadena. Indique qué queda fuera del alcance y por qué. Esto evita que una revisión limitada se confunda con una revisión del sistema completo.
Paso 2: Construir un punto de entrada y un mapa de activos.
Un inventario de funciones que separa los puntos de entrada públicos y externos antes de que el revisor rastree sus cambios de estado.
Enumera todas las funciones públicas y externas, incluidas las funciones heredadas y los manejadores de reserva o recepción. Para cada una, registra si puede:
transferir moneda o tokens nativos;
acuñar, quemar, pedir prestado, liquidar o alterar la contabilidad;
cambiar un oráculo, tarifa, límite, rol, estado de pausa o implementación;
realizar una llamada externa, delegar una llamada o realizar una llamada de bajo nivel; o
leer datos de los que depende otra función que cambia el estado.
Luego, mapea los activos y los límites de confianza. Sigue el depósito del usuario en el almacenamiento, pasando por el cálculo de precios y participaciones, hasta el retiro. Identifica cada dirección proporcionada por un llamante y cada dirección cargada desde el almacenamiento. Pregunta qué valores se consideran honestos: un oráculo, un token, un mensaje puente, un custodio, un receptor de devolución de llamada o un administrador. Los objetivos de revisión de mayor valor son las funciones que combinan la entrada controlada por el usuario, el estado privilegiado, la aritmética y una llamada externa.
Paso 3: Compilar correctamente y ejecutar el análisis estático.
Un análisis estático puede revelar rápidamente posibles problemas, que aún deben confirmarse con el código real y el modelo de amenazas.
Reproduce la compilación del proyecto con la versión de Solidity, las versiones de las dependencias, la configuración del optimizador y las suposiciones de la cadena de destino indicadas. Considera las advertencias del compilador como elementos de revisión, no como ruido sin importancia. Las consideraciones de seguridad de Solidity recomiendan específicamente tomar en serio las advertencias, mantener los contratos comprensibles y verificar los problemas conocidos del compilador. Consulta la lista oficial de errores conocidos del compilador de Solidity cuando la versión del compilador o los patrones de código afectados lo justifiquen.
Para un proyecto de Hardhat, Foundry o similar, ejecute Slither desde la raíz del proyecto. Su documentación oficial describe la herramienta como un analizador estático de Solidity y Vyper y proporciona el comando común:
slither .
Guarde la salida y clasifique cada resultado según su impacto y nivel de confianza. Examine detenidamente los hallazgos relacionados con envíos arbitrarios de tokens, actualizaciones no protegidas, reentrada, valores de retorno no verificados, delegatecall peligroso, tx.origin, aleatoriedad débil e interfaces incorrectas. Un detector puede reportar un falso positivo, pasar por alto una falla económica específica del proyecto o marcar código que está restringido intencionalmente en otro lugar. El análisis estático reduce la búsqueda; no reemplaza el razonamiento manual. El repositorio y la documentación de Slither también incluyen impresoras para puntos de entrada, autorización, gráficos de llamadas y resúmenes de contratos que ayudan a organizar la revisión.
Paso 4: Rastrear manualmente las llamadas externas y la reentrada.
Se destaca una llamada externa antes de una actualización de saldo, lo que ilustra la pregunta de ordenación que un revisor debe probar en cada ruta de retiro.
Para cada llamada externa, deténgase y rastree el estado antes, durante y después de la llamada. El destinatario puede ser un contrato malicioso, un token con ganchos, un receptor de devolución de llamada u otro protocolo que modifique una dependencia compartida. La documentación de Solidity explica que una interacción con otro contrato puede transferir el control a ese contrato y recomienda el patrón Comprobaciones-Efectos-Interacciones: validar primero, actualizar el estado de este contrato en segundo lugar e interactuar externamente al final.
No limite la búsqueda a transferencias de Ether obvias. Verifique los ganchos de estilo ERC-777, las devoluciones de llamada ERC-1155, las devoluciones de llamada de préstamos flash, los enrutadores arbitrarios, las llamadas a oráculos y las llamadas realizadas a través de bibliotecas heredadas. Revise la reentrada entre funciones y contratos: una devolución de llamada puede entrar en una función diferente que lee un estado intermedio. Confirme que cada llamada de bajo nivel verifica su resultado de éxito y maneja correctamente el valor devuelto. Pregunte si un destinatario fallido puede bloquear permanentemente los retiros o un bucle.
Registre una secuencia de ataque concreta para cada posible problema. Por ejemplo: el atacante realiza un depósito, inicia un retiro, recibe una notificación, vuelve a ingresar una segunda solicitud de retiro y solo entonces permite que finalice la primera. Si la secuencia no puede ejecutarse debido a una invariante o condición específica, anote el motivo. Esto permite verificar la conclusión en lugar de basarse en especulaciones.
Paso 5: Probar las invariantes aritméticas y comerciales.
Una lista de verificación aritmética y de lógica empresarial resalta los casos límite que las pruebas de escenarios normales suelen omitir.
Verifique el significado de cada unidad y conversión: wei frente a ether, decimales de tokens, puntos base, acciones frente a activos, valores con signo y unidades de tiempo. Siga la dirección del redondeo. Una división que redondea a favor de un depositante, prestatario, liquidador o receptor de comisiones puede generar pérdidas de valor al repetirse. Revise la multiplicación antes de la división, los montos mínimos y máximos, los límites de comisiones, los precios obsoletos, el suministro cero, el saldo cero y el primer depositante o último retirador.
Solidity 0.8 y versiones posteriores detectan normalmente el desbordamiento y el subdesbordamiento aritmético, pero el código dentro de un uncheckedbloque modifica deliberadamente este comportamiento. La aritmética controlada también puede provocar que un protocolo revierta o se vuelva inutilizable si los límites no están diseñados correctamente. Pruebe ambos resultados: robo o contabilidad incorrecta, y denegación de servicio causada por un valor que nunca se puede procesar.
Redacte las invariantes en lenguaje sencillo antes de convertirlas en pruebas. Algunos ejemplos son: «las acciones totales corresponden a los activos según la regla de redondeo establecida», «un usuario no puede retirar más de lo que ha reclamado», «el suministro total de tokens es igual a la suma de los saldos donde se aplica ese modelo» y «una comisión no puede exceder su límite configurado». Compare los saldos de almacenamiento con los saldos reales de tokens, ya que estos pueden enviarse directamente a un contrato o comportarse de forma diferente a la implementación ERC-20 prevista.
Paso 6: Revisar los permisos y la capacidad de actualización.
La revisión de privilegios debe vincular cada rol con su dirección, acción permitida, proceso de transferencia y ruta de actualización.
Cree una matriz de privilegios. Para cada función administrativa, identifique el rol requerido, el titular actual, el mecanismo de transferencia, el tiempo de espera, el control de multifirma o gobernanza y el comportamiento de emergencia. Preste especial atención a la creación, pausa, modificación de tarifas, cambio de fuentes de oráculo, recuperación de fondos, actualización de código y cambio de direcciones de tokens o enrutadores de confianza. La documentación de control de acceso de OpenZeppelin distingue entre la propiedad simple y los permisos basados en roles, y describe el principio de mínimo privilegio como una práctica de seguridad útil.
Separe la posibilidad de que un administrador realice esta acción del hecho de que un usuario cualquiera pueda hacerlo. La primera puede representar un riesgo explícito de gobernanza o custodia; la segunda, una vulnerabilidad de autorización. Verifique que las comprobaciones de roles cubran todas las rutas sensibles, incluidas las funciones internas accesibles desde funciones públicas. Compruebe si un administrador predeterminado puede otorgarse a sí mismo o a otros permisos adicionales, y si la transferencia de propiedad puede enviarse accidentalmente a una dirección inutilizable.
Para los proxies, revise el inicializador, la autorización de implementación, el retraso de actualización, la configuración de almacenamiento y el plan de reversión o emergencia. La guía de contratos actualizables de OpenZeppelin explica por qué los constructores no inicializan el almacenamiento del proxy, por qué los inicializadores deben estar protegidos, por qué una implementación no debe permanecer sin inicializar y por qué cambiar el orden o los tipos de almacenamiento puede dañar una actualización. Considere la clave de administrador del proxy como parte del límite de seguridad del protocolo, no como un detalle de implementación.
Paso 7: Ejercita el sistema con fuzzing, invariantes y bifurcaciones.
Las campañas de pases son una prueba útil, mientras que un rastro de contraejemplo muestra exactamente qué secuencia necesita ser investigada.
Ejecute pruebas unitarias para el comportamiento esperado y, a continuación, añada pruebas negativas para llamadas no autorizadas, valores cero, valores máximos, firmas caducadas, datos obsoletos del oráculo, transferencias fallidas y operaciones repetidas. Aplique pruebas de fuzzing a las entradas en lugar de probar solo unos pocos números seleccionados manualmente. Incluya múltiples actores y contratos de receptores maliciosos donde el diseño permita devoluciones de llamada.
Utilice pruebas de invariantes para propiedades que deben permanecer verdaderas tras numerosas llamadas aleatorias. La documentación de Foundry sobre pruebas de invariantes describe secuencias aleatorias, entradas difusas, ejecuciones, profundidad, contratos objetivo y remitentes objetivo. Configure los manejadores para que las llamadas sean significativas; si cada depósito difuso se revierte porque el actor de prueba no tiene tokens, una prueba de invariantes exitosa podría simplemente significar que no se produjo ningún cambio de estado útil.
Siempre que sea posible, utilice una bifurcación de la red objetivo para probar las direcciones implementadas, la configuración actual, el comportamiento del token y el enrutamiento del proxy. Mantenga las pruebas de la bifurcación seguras y de solo lectura, a menos que utilice una bifurcación local aislada. Minimice cada secuencia fallida y conserve el contraejemplo, las direcciones de las llamadas, el contexto del bloque, los saldos y los valores de almacenamiento relevantes. Una prueba exitosa es evidencia de las rutas probadas, no prueba de todas las rutas posibles.
Paso 8: Anote los hallazgos que se puedan corregir y volver a probar.
Un informe útil relaciona la gravedad y el estado con la evidencia, una solución específica y una condición para repetir la prueba.
Utilice un registro por problema. Un hallazgo práctico debe contener:
Título y ubicación: contrato, función, archivo y referencia de línea o código.
Impacto: qué se puede robar, congelar, inflar, eludir o hacer incorrecto.
Requisito previo: los permisos, saldos, tiempos o configuración necesarios.
Reproducción: una breve secuencia de transacciones, prueba, rastreo o comprobación.
Recomendación: un cambio específico en el código o en el funcionamiento, con sus respectivas ventajas e inconvenientes.
Estado: abierto, solucionado, mitigado, riesgo aceptado o no reproducible.
Repetición de la prueba: la prueba u observación exacta que confirma la resolución.
La gravedad debe reflejar el impacto real y la posibilidad de explotación, no lo alarmante que parezca un patrón de código. Explique las suposiciones. Una llamada de bajo nivel puede ser segura gracias a una invariante robusta; un cambio de parámetro aparentemente ordinario puede ser crítico si controla un oráculo o una actualización. Tras una corrección, revise las diferencias, vuelva a ejecutar la prueba correspondiente, vuelva a ejecutar el conjunto completo de pruebas y compruebe si hay regresiones. Si la dirección desplegada ya se actualizó o cambió, vuelva a probar la implementación y configuración reales en la cadena de bloques.
Errores comunes de auditoría que se deben evitar
“El código fuente está verificado, por lo que es seguro.” La verificación establece una correspondencia entre el código fuente y el código de bytes; no valida el diseño.
“El escáner no detectó nada, así que no hay errores.” Las herramientas son más eficaces con patrones conocidos, mientras que los fallos económicos y los problemas contractuales suelen requerir análisis humano.
“El proyecto cuenta con un informe de auditoría, por lo que la implementación actual está cubierta.” Compare el informe con las confirmaciones, el alcance, las direcciones de implementación, las correcciones y el historial de actualizaciones.
“Las pruebas de fuzzing se superaron, por lo que el invariante es correcto”. Primero, confirme que el invariante expresa la propiedad económica prevista y que los manejadores alcanzan estados significativos.
“El control administrativo no es un problema de seguridad”. Puede que se trate de una suposición de confianza intencionada, pero los usuarios deberían poder ver quién puede crear, pausar, cambiar parámetros o actualizar.
Última autocomprobación antes de confiar en el resultado.
Deberías poder responder afirmativamente a estas preguntas:
¿Registré la cadena, la dirección, el código de bytes, el proxy, la implementación y la configuración de compilación exactos?
¿He inventariado todos los puntos de entrada que modifican el estado del sistema y los activos que pueden verse afectados?
¿Compilé correctamente, revisé las advertencias y clasifiqué los hallazgos automatizados?
¿He rastreado todas las llamadas externas, las devoluciones de llamada, las llamadas de bajo nivel y las rutas de fallo?
¿He probado el redondeo, los límites, los valores cero, los datos obsoletos y las acciones repetidas?
¿He mapeado todos los roles privilegiados, claves, retrasos, inicializadores y rutas de actualización?
¿He preservado la imprecisión significativa y los contraejemplos invariantes?
¿Puede un revisor independiente reproducir cada hallazgo y verificar cada corrección?
Si alguna respuesta es negativa, indique que la auditoría está incompleta y especifique la evidencia faltante. Una limitación clara es más útil que una conclusión vaga de "seguridad". La seguridad de los contratos inteligentes es un proceso continuo: cada actualización, cambio de dependencia, nueva integración y cambio de privilegios puede generar un nuevo límite de revisión.