Acabamos de ver cómo una pequeña laguna conduce a una pérdida financiera (de diversa magnitud), de la misma manera que los contratos inteligentes desarrollados sobre Solidity son propensos a varios ataques conocidos y desconocidos. Los explotadores aprovechan los errores y las lagunas para echar un vistazo a los contratos inteligentes y manipularlos para llevar a cabo ataques. Aquí presentamos una lista completa de los 5 errores más comunes en el lenguaje de programación Solidity.
En QuillAudits seguimos la metodología adaptativa para obtener la esencia de cada truco e implementar sus aprendizajes en futuros contratos inteligentes para evitar cualquier amenaza potencial.
Errores en el lenguaje de programación de solidez
1. Llamada externa sin marcar
Estamos eliminando este problema en primer lugar porque es uno de los escollos de Solidity más comúnmente observados. Generalmente, el envío de ether a cualquier cuenta externa se realiza a través del transferir() función. Aparte de esto, las dos funciones más utilizadas para realizar una llamada externa son; llamada()y enviar (), aquí principalmente el llamada() La función se utiliza ampliamente para realizar llamadas externas versátiles por parte de los desarrolladores.
Aunque el llamada() y enviar () Las funciones devuelven un valor booleano que especifica si la llamada fue exitosa o no. Por tanto, en este caso, si alguna de las funciones llamada() or enviar () no logra realizar la tarea, se revertirán con un falso. Por lo tanto, si el desarrollador no verifica el valor de retorno, se convertiría en una trampa.
La vulnerabilidad
Considere el siguiente ejemplo:
contrato Lotto {
boolpublic payedOut = false;
dirección pública ganadora;
cantidad de ganancia uintpublic;
// ... funcionalidad adicional aquí
function sendToWinner () public {
require (! payedOut);
ganador.enviar (winAmount);
payedOut = verdadero;
}
function retirewLeftOver () public {
require (payedOut);
msg.sender.send (este.balance);
}
}
En el contrato tipo Lotto anterior, podemos observar que un ganador recibe cantidad ganadora de éter dejando un poco de sobra para retirar de cualquier agente externo.
Aquí, el problema del contrato existe en la línea [11], donde un envío se utiliza sin validación cruzada de la respuesta. En el ejemplo anterior, un ganador cuya transacción falla (ya sea por deficiencia de Gas o si es un contrato que intencionalmente incluye la función de respaldo), autoriza pagado para ser configurado en verdadero independientemente de si la transacción de ether fue un éxito o no. En este caso, cualquier explotador puede retirar el ganador ganancias a través del retirarseLeftOver función.
Enfoque de QuillAudit
Nuestro equipo interno de desarrolladores aborda este error con el uso de [transferir] función en lugar de [enviar] función, ya que [transferencia] se revertirá si se revierte la transacción externa. Y si está utilizando [enviar], siempre verifique el valor devuelto.
Uno de los enfoques sólidos que seguimos es utilizar un [patrón de retiro]. Aquí, aislamos lógicamente la funcionalidad de envío externo del resto de la base de código y colocamos la tensión de las transacciones potencialmente fallidas en el usuario final, ya que es él quien llama a la función de retiro.
2. Reingreso
Los contratos inteligentes de Ethereum llaman y utilizan códigos de otros contratos externos, y para llevar a cabo esto, los contratos deben enviar llamadas externas. Estas llamadas externas son vulnerables y propensas a ataques, uno de esos ataques tuvo lugar recientemente en el caso del hackeo de DAO.
La vulnerabilidad
Los atacantes llevan a cabo estos ataques cuando un contrato envía ether a una dirección desconocida. En este caso, el atacante puede crear un contrato en una dirección externa que posea código malicioso en la función de reserva, y este código malicioso será invocado cuando el contrato envíe ether a esta dirección.
Hecho: El término "reentrada" se ha acuñado por el hecho de que cuando un contrato malicioso externo llama a una función sobre el contrato vulnerable y luego la ruta de ejecución del código "vuelve a entrar" en él.
Considere el siguiente ejemplo, es una bóveda de Ethereum que permite a los depositantes retirar solo 1 éter por semana.
contrato EtherStore {
uint256 public retiroLimit = 1 éter;
mapeo (dirección => uint256) public lastWithdrawTime;
mapeo (dirección => uint256) saldos públicos;
función depositFunds () pago externo {
saldos [msg.sender] + = msg.value;
}
función retire fondos (uint256 _weiToWithdraw) public {
require (saldos [msg.sender]> = _weiToWithdraw);
// limitar la retirada
require (_weiToWithdraw <= retiroLimit);
// limitar el tiempo permitido para retirarse
require (ahora> = lastWithdrawTime [msg.sender] + 1 semana);
require (msg.sender.call.value (_weiToWithdraw) ());
saldos [msg.sender] - = _weiToWithdraw;
lastWithdrawTime [msg.sender] = ahora;
}
}
En el contrato anterior, tenemos dos funciones públicas, [depositar fondos] y [retirar fondos]. Los [depositFunds] se utilizan para incrementar el saldo del remitente, mientras que [retirewFunds] especifica la cantidad a retirar. En este caso, será un éxito si la cantidad a retirar es inferior a 1 éter.
La trampa aquí se encuentra en la línea [17] donde tiene lugar la transferencia de éter. El atacante podría crear un contrato malicioso con la dirección del contrato de [EtherStores] como único parámetro del constructor. Esto haría que [etherStore] sea una variable pública, por lo tanto, más propensa a ser atacada.
Enfoque de QuilllAudit
Seguimos varias técnicas para evitar posibles vulnerabilidades de reentrada en contratos inteligentes. La primera y mejor forma posible es el uso de la función [transferencia] incorporada al transferir ether a cualquier contrato externo.
En segundo lugar, es importante asegurarse de que todos los cambios lógicos en las variables de estado deben realizarse antes de enviar ether fuera del contrato. En el ejemplo de [EtherStore], las líneas [18] y [19] deben colocarse antes de la línea [17].
También se puede utilizar una tercera técnica para evitar llamadas reentrantes; mediante la introducción de un mutex. Es una adición de una variable de estado que bloqueará el contrato durante la ejecución del código.
3. Visibilidades predeterminadas
Hay especificadores de visibilidad para las funciones que usamos en Solidity, y prescriben la forma en que se pueden llamar. Es la visibilidad la que determina la llamada de las funciones; externamente por los usuarios, por otros contratos derivados, solo internamente o solo externamente. Veamos cómo el uso erróneo de especificadores de visibilidad puede causar una gran vulnerabilidad en los contratos inteligentes.
La vulnerabilidad
Por defecto, la visibilidad de la función es [pública], por lo que los usuarios externos pueden llamar a las funciones sin visibilidad específica. El error surge cuando los desarrolladores olvidan especificar la visibilidad de las funciones que deberían ser privadas (o que se pueden llamar dentro del contrato mismo). Por ejemplo;
contrato HashForEther {
function retireWinnings () {
// Ganador si los últimos 8 caracteres hexadecimales de la dirección son 0
require (uint32 (msg.sender) == 0);
_sendWinnings ();
}
function _sendWinnings () {
msg.sender.transfer (este.balance);
}
}
El contrato anterior es un simple juego de recompensas para adivinar direcciones. En esto, podemos ver que la visibilidad de las funciones no está especificada, particularmente la función [_sendWinnings] es [pública] (por defecto), por lo que se puede llamar a través de cualquier dirección para robar la recompensa.
Enfoque de QuillAudit
Nuestro equipo interno está formado por desarrolladores experimentados que siempre siguen las mejores prácticas de auditoría, aquí la visibilidad de las funciones debe especificarse explícitamente, incluso si se van a mantener públicas, debe mencionarse.
4. Salvaguardar el uso de constructores
Generalmente, los constructores se denominan funciones especiales que se utilizan para realizar tareas críticas y privilegiadas al inicializar los contratos. Antes de Solidity [v0.4.22], los constructores tenían el mismo nombre utilizado en el contrato que los contenía. Ahora, considere un caso en el que el nombre del contrato se cambia durante la fase de desarrollo pero el nombre del constructor sigue siendo el mismo, esta laguna también puede proporcionar a los atacantes una entrada fácil a su contrato inteligente.
La vulnerabilidad
Puede tener consecuencias graves si se modifica el nombre del contrato pero no se modifica el nombre del constructor. Por ejemplo:
contrato OwnerWallet {
dirección pública propietario;
// constructor
función ownerWallet (dirección _owner) public {
propietario = _propietario;
}
// Retroceder. Recoge éter.
función () pagadero {}
función retirar () público {
require (msg.sender == propietario);
msg.sender.transfer (este.balance);
}
}
En el contrato anterior, podemos ver que solo el propietario puede retirar ether llamando a la función [retirar]. Aquí, la vulnerabilidad se produce cuando el nombre del constructor es diferente al del contrato (¡la primera letra es diferente!). Por lo tanto, el explotador puede llamar a la función [ownerWallet] y autorizarse como propietario, y luego retirar todo el ether en el contrato llamando a [retirar].
Enfoque de QuillAudit
Cumplimos con la versión [0.4.22] del compilador Solidity. Esta versión ha introducido una palabra clave; [constructor] que requiere que el nombre de la función coincida con el nombre del contrato.
5. Autenticación de Tx.Origin
Aquí, [Tx.Origin] es la variable global de Solidity, contiene la dirección de la cuenta que ejecutó originalmente la llamada o transacción. Esta variable no se puede utilizar para la autenticación, ya que hacerlo hace que el contrato sea vulnerable a los ataques de phishing.
La vulnerabilidad
Los contratos que autorizan a los usuarios a través de la variable [tx.origin] están expuestos a ataques externos que llevan a los usuarios a realizar acciones autenticadas sobre el contrato erróneo. Considere el siguiente ejemplo:
contrato Phishable {
dirección pública propietario;
constructor (dirección _owner) {
propietario = _propietario;
}
function () external payable {} // recolectar ether
función retiretodos (dirección _recipient) public {
require (tx.origin == propietario);
_recipient.transfer (este.balance);
}
}
Aquí, en la línea [11], el contrato autoriza la función [retirar todo] con la ayuda de [tx.origin].
Enfoque de QuillAudit
Generalmente, evitamos usar [tx.origin] para la autorización en contratos inteligentes. Aunque el uso de [tx.origin] no está estrictamente prohibido, tiene algunos casos de uso específicos. Podemos usar [tx.origin] para denegar que los contratos externos llamen al presente contrato, se puede ejecutar con [require] de la forma [require (tx.origin == msg.sender)]. Se hace para evitar la llamada de contratos intermedios para llamar al contrato actual que limita el contrato a direcciones regulares sin código.
Conclusión final
Hemos cubierto exhaustivamente los cinco errores comunes en el lenguaje de Solidity. Al desarrollar contratos inteligentes, no debemos olvidar que son inmutables por diseño, lo que significa que una vez que los creamos, no hay forma de parchear el código fuente.
Esto representa un gran desafío para los desarrolladores para aprovechar las herramientas de auditoría y pruebas de seguridad disponibles antes de la implementación.
Nuestro equipo interno de expertos en auditoría lleva a cabo el descubrimiento de posibles amenazas maliciosas para los contratos inteligentes y los riesgos, algunos de los cuales mencionamos anteriormente. En QuillAudits ponemos nuestros mejores esfuerzos en la investigación de seguridad para mantener su contrato actualizado con todas las prácticas de seguridad de software para mantener su contrato seguro y protegido.
Comuníquese con QuillHash
Con una presencia industrial de años, QuillHash ha entregado soluciones empresariales en todo el mundo. QuillHash con un equipo de expertos es una empresa líder en desarrollo de blockchain que ofrece varias soluciones de la industria, incluida la empresa DeFi.Si necesita ayuda en la auditoría de contratos inteligentes, no dude en comunicarse con nuestros expertos. aquí!
Siga QuillHash para obtener más actualizaciones
Twitter | Etiqueta LinkedIn | Facebook
Fuente: https://blog.quillhash.com/2021/06/04/top-5-common-errors-in-solidity-programming-language/
- 11
- Mi Cuenta
- Ventaja
- Todos
- auditoría
- Autenticación
- autorización
- MEJOR
- blockchain
- Error
- loco
- llamar al
- cases
- Causar
- Reto
- código
- Algunos
- compañía
- contrato
- contratos
- Current
- DAO
- DeFi
- Diseño
- Developer
- desarrolladores
- Desarrollo
- Empresa
- Éter
- Etereum
- Evento
- expertos
- financiero
- Nombre
- seguir
- formulario
- Gratuito
- función
- futuras
- juego
- GAS
- Buscar
- maravillosa
- corte
- esta página
- Cómo
- HTTPS
- enorme
- Incluye
- energético
- IT
- idioma
- Lead
- líder
- línea
- Etiqueta LinkedIn
- Lista
- Match
- Otro
- propietario
- Patch
- Patrón de Costura
- suplantación de identidad
- ataques de phishing
- prescribir
- presente
- privada
- Programación
- público
- tracción
- la investigación
- respuesta
- RESTO
- ambiente seguro
- EN LINEA
- Servicios
- set
- sencillos
- chica
- inteligente
- contrato inteligente
- Contratos Inteligentes
- So
- Software
- solidez
- Soluciones
- Estado
- comercial
- Pruebas
- La Fuente
- amenazas
- equipo
- parte superior
- top 5
- transaccional
- Transacciones
- us
- usuarios
- propuesta de
- Bóveda
- la visibilidad
- Vulnerabilidades
- vulnerabilidad
- Vulnerable
- semana
- QUIENES
- dentro de
- años