Lección 4: Cómo proteger sus contratos inteligentes

Ethereum Development 101: Lección 4: Asegurar sus contratos inteligentes contra ataques de reentrada y robo de propietarios

Requisito previo: Este artículo es parte de Ethereum Development 101 , un curso diseñado para enseñar los conceptos básicos de desarrollo, prueba e implementación de contratos inteligentes en la red Ethereum.

Objetivos de aprendizaje: Al final de esta lección, deberías poder comprender las vulnerabilidades de reentrada y robo del propietario en los contratos inteligentes de Solidity. Debería poder escribir código que lo proteja contra estas vulnerabilidades.

Descripción general

Visión de túnel, un error que todos los desarrolladores han experimentado. Al desarrollar un nuevo código para un propósito específico, es fácil concentrarse tanto en resolver un problema en particular que nos perdamos algo importante.

Esto es especialmente cierto cuando se pasa de una tecnología a otra. Por ejemplo: si tiene experiencia en javascript, es poco probable que le preocupe mucho la explotación del desbordamiento, pero en Solidity es necesario abordarlo.

Vamos a pasar por algunas debilidades que son inherentes a Solidity: Hacks de reentrada y Robo de lógica del propietario .

Ataques de reentrada

Qué

Los contratos inteligentes a menudo necesitarán llamar o enviar ether a una dirección externa. Este tipo de operación es inherentemente vulnerable a la reentrada.

Para realizar un ataque de reentrada, el atacante implementa un contrato malicioso en la red. Este contrato intenta manipular la lógica del contrato objetivo para enviarle Ether, invocando así su función de respaldo. Luego, la función de respaldo recupera el contrato objetivo durante la ejecución, para drenar el contrato objetivo de Ether.

Foto de Bret Kavanaugh en Unsplash

Cómo

Tome la siguiente función dentro de un contrato objetivo vulnerable:

Suponiendo que saldos es una asignación de direcciones a valores enteros, la función realiza las siguientes acciones:

Siempre que la función de retirada sea llamada por una dirección sin contrato, la lógica está bien. Sin embargo, si el remitente es una dirección de contrato, esta función se puede aprovechar para agotar el contrato.

Aquí hay una función alternativa simple dentro de nuestro contrato malicioso :

Suponga que llamadas es una variable de estado definida como 0 en el constructor. Cuando el contrato objetivo intenta enviar Ether a la dirección maliciosa donde reside este contrato, se llama a esta función de reserva. Si llamadas es menor que 10, se volverá a llamar a la función retiro () en el contrato target . Esto se repite de forma recursiva hasta que se llama 10 veces, lo que agota el contrato de objetivo 10 veces más de lo previsto.

Esto sucedió en la vida real. En 2016, el hack de DAO causó grandes ondas de choque en Ethereum y la industria Blockchain. Sus consecuencias todavía se sienten hoy.

Prevención de ataques de reentrada

Hay varias formas de proteger sus contratos de los ataques de reentrada.

La primera es usar para enviar Ether . En versiones anteriores de Solidity, los contratos necesitaban utilizar el método call () , en el que no se establecía ningún límite de gas de forma predeterminada. transfer () por otro lado actualmente tiene un límite de 2,300 unidades de gas, que es suficiente para emitir un evento. Con ese límite, cualquier ejecución recursiva compleja se quedaría sin gas casi de inmediato.

Utilice el patrón Comprobaciones → Efectos → Interacciones . Al escribir código para operaciones confidenciales, asegúrese inicialmente de:

Otra técnica es bloquear el contrato , también conocido como mutex. Utilice una variable de estado para determinar si se está ejecutando actualmente una función de contrato. Si el código está bloqueado, no se puede llamar a ninguna función hasta que se libere el bloqueo.

Robo de lógica del propietario

Qué

Los modificadores de funciones permiten a los desarrolladores identificar quién puede llamar a cada función en un contrato en función de su privilegio. Donde las palabras clave public y private son comunes para los desarrolladores de otros lenguajes, Solidity también permite modificadores personalizados.

Un patrón de modificador común es onlyOwner . Aquí es donde una variable de estado owner registra la dirección que implementó el contrato. Cualquier función con este modificador requiere que la dirección de la persona que llama sea igual al propietario .

El modificador personalizado tiene este aspecto:

Las funciones que usan este modificador pueden declararse así:

función algoImportante () solo propietario público {…}

Convertirse en propietario de un contrato es poderoso, ya que el propietario suele tener los privilegios más altos para interactuar con el contrato.

Cómo

Los contratos de OnlyOwner a menudo tendrán una función para cambiar el propietario:

El problema aquí es que esta función es pública , lo que significa que cualquiera puede llamar a esta función y, por lo tanto, convertirse en el propietario del contrato. Se debe aplicar un modificador adicional de onlyOwner a la declaración de función.

Prevención del robo de propietarios

Siga las buenas prácticas y siempre use los modificadores correctos para las funciones . No hace falta decirlo, pero como se mencionó anteriormente, la visión de túnel a veces puede contribuir a dejar que problemas como estos se escapen de la red.

Obtenga una segunda opinión . Hay muchas comunidades y empresas que realizan auditorías de contratos inteligentes. Si está implementando contratos confidenciales, le recomiendo que utilice estos servicios.

Utilice bibliotecas estándar de la industria . OpenZeppellin tiene un conjunto de bibliotecas y contratos que la comunidad audita exhaustivamente antes de su lanzamiento.

Conclusión

Reentrancy es el exploit de más alto perfil debido al hack de DAO. Se han tomado medidas para mitigar el riesgo, como la introducción de transfer () , pero aún debe tener cuidado y codificar en consecuencia.

Utilice siempre las mejores prácticas, los estándares de la industria y las bibliotecas aceptadas por la comunidad. Heredar contratos examinados por muchos desarrolladores significa que puede tener más confianza en que funcionan según lo diseñado.

Utilice herramientas de análisis estático y audite su código.

Por favor, agregue cualquier pregunta en los comentarios a continuación. Si ha terminado con esta lección, vaya directamente a la siguiente:

Alternativamente, regrese a la página del curso Ethereum Development 101 para elegir otra lección: