Nous venons de voir comment une petite faille entraîne une perte financière (d'ampleur variable), de la même manière que les contrats intelligents développés sur Solidity sont sujets à diverses attaques connues et inconnues. Les exploiteurs profitent des bugs et des failles pour jeter un coup d'œil dans les contrats intelligents et les manipuler pour mener des attaques. Nous présentons ici une liste complète des 5 erreurs les plus courantes rencontrées dans le langage de programmation Solidity.
Chez QuillAudits, nous suivons la méthodologie adaptative pour comprendre l'essentiel de chaque piratage et mettre en œuvre ses apprentissages sur les futurs contrats intelligents afin d'éviter toute menace potentielle.
Erreurs dans le langage de programmation de solidité
1. Appel externe non coché
Nous tirons ce problème en premier lieu car il s'agit de l'un des pièges de Solidity les plus couramment observés. Généralement, envoyer de l'éther vers n'importe quel compte externe s'effectue via le transfert() une fonction. En dehors de cela, les deux fonctions les plus utilisées pour passer un appel externe sont : appel()et envoyer (), ici principalement le appel() La fonction est largement utilisée pour effectuer des appels externes polyvalents par les développeurs.
Bien que le appel() ainsi que envoyer () les fonctions renvoient une valeur booléenne spécifiant si l'appel a réussi ou non. Ainsi, dans ce cas, si l'une des fonctions appel() or envoyer () n'exécute pas la tâche, ils reviendront avec un faux Par conséquent, si le développeur ne contre-vérifie pas la valeur de retour, cela deviendrait un piège.
La vulnérabilité
Considérez l'exemple ci-dessous :
contrat Loto{
boolpublic payedOut =false ;
s'adresser au public gagnant ;
uintpublic winAmount ;
// … des fonctionnalités supplémentaires ici
fonction sendToWinner()public{
exiger(!payé);
gagnant.send(winAmount);
payéOut =true ;
}
fonction retirerLeftOver()public{
exiger (payé);
msg.sender.send(this.balance);
}
}
Dans le contrat de type Loto ci-dessus, nous pouvons observer qu'un gagnant reçoit winMontant d'éther laissant un peu de reste à retirer de tout agent extérieur.
Ici, l'écueil du contrat existe à la ligne [11], où un envoyer est utilisé sans validation croisée de la réponse. Dans l'exemple ci-dessus, un gagnant dont la transaction échoue (soit par manque de Gaz soit s'il s'agit d'un contrat qui lance intentionnellement la fonction de repli), autorise payé être réglé sur oui indépendamment du fait que la transaction d'éther ait été un succès ou non. Dans ce cas, tout exploiteur peut retirer le gagnants gains via le retirerLeftOver la fonction.
L'approche de QuillAudit
Notre équipe interne de développeurs s'attaque à ce bug avec l'utilisation de [transfert] fonction au lieu de [envoyer] fonction, car [transfert] sera annulé si la transaction externe est annulée. Et si vous utilisez [send], vérifiez toujours la valeur de retour.
L'une des approches robustes que nous suivons utilise un [modèle de retrait]. Ici, nous isolons logiquement la fonctionnalité d'envoi externe du reste de la base de code et plaçons la pression des transactions potentiellement échouées sur l'utilisateur final, car c'est lui qui appelle la fonction de retrait.
2. Rentrée
Les contrats intelligents Ethereum appellent et utilisent les codes d'autres contrats externes, et pour ce faire, les contrats sont tenus de soumettre des appels externes. Ces appels externes sont vulnérables et sujets aux attaques, une telle attaque a eu lieu récemment dans le cas du piratage DAO.
La vulnérabilité
Les attaquants effectuent de telles attaques lorsqu'un contrat envoie de l'éther à une adresse inconnue. Dans ce cas, l'attaquant peut créer un contrat à une adresse externe qui possède un code malveillant dans la fonction de secours, et ce code malveillant sera invoqué lorsque le contrat envoie de l'éther à cette adresse.
Fait: Le terme « Reentrancy » a été inventé du fait que lorsqu'un contrat malveillant externe appelle une fonction sur le contrat vulnérable, le chemin d'exécution du code y « entre ».
Considérez l'exemple ci-dessous, il s'agit d'un coffre-fort Ethereum qui permet aux déposants de ne retirer qu'un seul éther par semaine.
contrat EtherStore {
uint256 public retireLimit = 1 éther;
mapping(adresse => uint256) public lastWithdrawTime;
mappage (adresse => uint256) soldes publics ;
function depositFunds() externe payable {
soldes[msg.sender] += msg.value;
}
fonction removeFunds (uint256 _weiToWithdraw) public {
require(balances[msg.sender] >= _weiToWithdraw);
// limiter le retrait
require(_weiToWithdraw <= retireLimit);
// limite le délai de rétractation
require(maintenant >= lastWithdrawTime[msg.sender] + 1 semaines);
require(msg.sender.call.value(_weiToWithdraw)());
soldes[msg.sender] -= _weiToWithdraw;
lastWithdrawTime[msg.sender] = maintenant;
}
}
Dans le contrat ci-dessus, nous avons deux fonctions publiques, [depositFunds] et [withdrawFunds]. Le [depositFunds] est utilisé pour incrémenter le solde de l'expéditeur, tandis que [withdrawFunds] spécifie le montant à retirer. Dans ce cas, ce sera un succès si le montant à retirer est inférieur à 1 éther.
L'écueil réside ici dans la ligne [17] où s'effectue le transfert d'éther. L'attaquant pourrait créer un contrat malveillant avec l'adresse de contrat de [EtherStores] comme seul paramètre de constructeur. Cela ferait de [etherStore] une variable publique, donc plus susceptible d'être attaquée.
Approche de QuillAudit
Nous suivons diverses techniques pour éviter les vulnérabilités potentielles de réentrance dans les contrats intelligents. Le tout premier et le meilleur moyen possible est d'utiliser la fonction [transfert] intégrée lors du transfert d'éther vers un contrat externe.
Deuxièmement, il est important de s'assurer que tous les changements logiques dans les variables d'état doivent être effectués avant d'envoyer l'éther hors du contrat. Dans l'exemple [EtherStore], les lignes [18] et [19] doivent être placées avant la ligne [17].
Une troisième technique peut également être utilisée pour empêcher les appels réentrants ; par l'introduction d'un mutex. Il s'agit d'un ajout d'une variable d'état qui verrouillera le contrat lors de l'exécution du code.
3. Visibilités par défaut
Il existe des spécificateurs de visibilité pour les fonctions que nous utilisons dans Solidity, et ils prescrivent la façon dont elles peuvent être appelées. C'est la visibilité qui détermine l'appel des fonctions ; en externe par les utilisateurs, par d'autres contrats dérivés, uniquement en interne ou uniquement en externe. Voyons comment une utilisation erronée des spécificateurs de visibilité peut entraîner une énorme vulnérabilité dans les contrats intelligents.
La vulnérabilité
Par défaut, la visibilité de la fonction est [public], les utilisateurs externes peuvent donc appeler les fonctions sans visibilité spécifique. Le bogue survient lorsque les développeurs oublient de spécifier la visibilité sur les fonctions qui doivent être privées (ou peuvent être appelées dans le contrat lui-même). Par example;
contrat HashForEther {
fonction retirerGagnes () {
// Gagnant si les 8 derniers caractères hexadécimaux de l'adresse sont 0
require(uint32(msg.sender) == 0);
_sendWinnings();
}
fonction _sendWinnings() {
msg.sender.transfer(this.balance);
}
}
Le contrat ci-dessus est un simple jeu de devinettes d'adresse. En cela, nous pouvons voir que la visibilité des fonctions n'est pas spécifiée, en particulier la fonction [ _sendWinnings] est [public] (par défaut), elle peut donc être appelée via n'importe quelle adresse pour voler la prime.
L'approche de QuillAudit
Notre équipe interne est composée de développeurs aguerris qui suivent toujours les meilleures pratiques d'audit, ici la visibilité des fonctions doit être spécifiée explicitement, même si elles doivent rester publiques, il convient de le mentionner.
4. Protection de l'utilisation des constructeurs
Généralement, les constructeurs sont appelés fonctions spéciales qui sont utilisées pour effectuer des tâches critiques et privilégiées lors de l'initialisation des contrats. Avant Solidity [v0.4.22], les constructeurs portaient le même nom que celui utilisé par le contrat qui les contenait. Maintenant, considérons un cas où le nom du contrat est modifié pendant la phase de développement mais le nom du constructeur reste le même, cette échappatoire peut également fournir aux attaquants un accès facile à votre contrat intelligent.
La vulnérabilité
Cela peut entraîner de graves conséquences si le nom du contrat est modifié mais que le nom du constructeur reste inchangé. Par example:
contrat OwnerWallet {
adresse publique propriétaire;
// constructeur
function ownerWallet(adresse _owner) public {
propriétaire = _propriétaire ;
}
// Se retirer. Recueillir de l'éther.
fonction () payable {}
fonction retirer() public {
require(msg.sender == propriétaire);
msg.sender.transfer(this.balance);
}
}
Dans le contrat ci-dessus, nous pouvons voir que seul le propriétaire peut retirer de l'éther en appelant la fonction [retirer]. Ici, la vulnérabilité se produit car le constructeur est nommé différemment du contrat (la première lettre est différente !). Ainsi, l'exploiteur peut appeler la fonction [ownerWallet] et s'autoriser en tant que propriétaire, puis retirer tout l'éther du contrat en appelant [retirer].
L'approche de QuillAudit
Nous respectons la version [0.4.22] du compilateur Solidity. Cette version a introduit un mot-clé ; [constructeur] qui requiert que le nom de la fonction corresponde au nom du contrat.
5. Authentification Tx.Origin
Ici, [Tx.Origin] est la variable globale de Solidity, elle contient l'adresse du compte qui a initialement exécuté l'appel ou la transaction. Cette variable ne peut pas être utilisée pour l'authentification, car cela rend le contrat vulnérable aux attaques de phishing.
La vulnérabilité
Les contrats autorisant les utilisateurs via la variable [tx.origin] sont exposés à des attaques externes amenant les utilisateurs à effectuer des actions authentifiées sur le contrat erroné. Considérez l'exemple ci-dessous :
contrat Phishable {
adresse publique propriétaire;
constructeur (adresse _propriétaire) {
propriétaire = _propriétaire ;
}
function () externe payable {} // collecte de l'éther
fonction retirerTous (adresse _destinataire) public {
require(tx.origin == propriétaire);
_recipient.transfer(this.balance);
}
}
Ici à la ligne [11], le contrat autorise la fonction [withdrawAll] à l'aide de [tx.origin].
L'approche de QuillAudit
Nous évitons généralement d'utiliser [tx.origin] pour l'autorisation dans les contrats intelligents. Bien que l'utilisation de [tx.origin] ne soit pas strictement interdite, il existe des cas d'utilisation spécifiques. Nous pouvons utiliser [tx.origin] pour empêcher les contrats externes d'appeler le présent contrat, il peut être exécuté avec [require] de la forme [require(tx.origin == msg.sender)]. Il est fait pour éviter l'appel de contrats intermédiaires pour appeler le contrat en cours qui limite le contrat aux adresses sans code régulières.
Conclusion finale
Nous avons couvert en détail les cinq pièges courants du langage Solidity. Lors du développement de contrats intelligents, nous ne devons pas oublier qu'ils sont immuables par conception, ce qui signifie qu'une fois que nous les avons créés, il n'y a aucun moyen de corriger le code source.
Cela pose un grand défi aux développeurs pour tirer parti des outils de test de sécurité et d'audit disponibles avant le déploiement.
La découverte des menaces malveillantes potentielles pour les contrats intelligents et les risques dont certains que nous avons mentionnés ci-dessus sont effectuées de manière tout à fait unique et robuste par notre équipe interne d'experts en audit. Chez QuillAudits, nous mettons nos meilleurs efforts dans la recherche de sécurité pour maintenir votre contrat à jour avec toutes les pratiques de sécurité logicielle pour garder votre contrat sûr et sécurisé.
Contactez QuillHash
Avec une présence dans l'industrie depuis des années, QuillHash a fourni des solutions d'entreprise à travers le monde. QuillHash avec une équipe d'experts est une société de développement de chaînes de blocs de premier plan fournissant diverses solutions industrielles, y compris l'entreprise DeFi.Si vous avez besoin d'aide dans l'audit des contrats intelligents, n'hésitez pas à contacter nos experts ici!
Suivez QuillHash pour plus de mises à jour
Source : https://blog.quillhash.com/2021/06/04/top-5-common-errors-in-solidity-programming-language/
- 11
- Compte
- Avantage
- Tous
- audit
- Authentification
- autorisation
- LES MEILLEURS
- blockchain
- Punaise
- bogues
- Appelez-nous
- cas
- Causes
- challenge
- code
- Commun
- Société
- contrat
- contrats
- Courant
- DAO
- DeFi
- Conception
- Développeur
- mobiles
- Développement
- Entreprise
- Éther
- Ethereum
- événement
- de santé
- la traduction de documents financiers
- Prénom
- suivre
- formulaire
- Test d'anglais
- fonction
- avenir
- jeu
- GAS
- Global
- l'
- entaille
- ici
- Comment
- HTTPS
- majeur
- Y compris
- industrie
- IT
- langue
- conduire
- conduisant
- Gamme
- Liste
- Match
- Autre
- propriétaire
- Pièce
- Patron de Couture
- phishing
- attaques de phishing
- prescrire
- représentent
- Privé
- Programmation
- public
- tirant
- un article
- réponse
- REST
- des
- sécurité
- Services
- set
- étapes
- petit
- smart
- contrat intelligent
- Contrats intelligents
- So
- Logiciels
- solidité
- Solutions
- Région
- succès
- Essais
- La Source
- des menaces
- fiable
- top
- haut 5
- transaction
- Transactions
- us
- utilisateurs
- Plus-value
- Voûte
- définition
- vulnérabilités
- vulnérabilité
- Vulnérable
- semaine
- WHO
- dans les
- années