
Solidity Security
Cet article se veut une introduction relativement approfondie et à jour qui détaille les erreurs passées commises par les développeurs Solidity, afin d'empêcher les futurs développeurs de répéter l'histoire.
L'une des caractéristiques des contrats intelligents Ethereum est la capacité d'appeler et d'utiliser le code d'autres contrats externes. Les contrats gèrent également généralement de l'ether et, à ce titre, envoient souvent de l'ether à diverses adresses d'utilisateurs externes. L'opération d'appel de contrats externes, ou d'envoi d'ether à une adresse, nécessite que le contrat soumette un appel externe. Ces appels externes peuvent être détournés par des attaquants qui forcent alors le contrat à exécuter du code supplémentaire (par exemple via une fonction fallback), y compris des appels vers lui-même. Ainsi, l'exécution du code « ré-entre » dans le contrat. Ce type d'attaque a été utilisé lors du tristement célèbre hack de The DAO.
Pour en savoir plus sur les attaques par ré-entrance, consultez Reentrancy Attack On Smart Contracts et Consensus - Ethereum Smart Contract Best Practices.
Cette attaque peut se produire lorsqu'un contrat envoie de l'ether à une adresse inconnue. Un attaquant peut soigneusement construire un contrat à une adresse externe contenant du code malveillant dans la fonction fallback. Ainsi, lorsqu'un contrat envoie de l'ether à cette adresse, il invoque le code malveillant. En général, le code malveillant exécute une fonction sur le contrat vulnérable, effectuant des opérations non attendues par le développeur. Le nom « ré-entrance » vient du fait que le contrat externe malveillant rappelle une fonction du contrat vulnérable et « ré-entre » dans l'exécution du code à un emplacement arbitraire du contrat vulnérable.
Pour clarifier cela, considérons le simple contrat vulnérable suivant, qui agit comme un coffre-fort Ethereum permettant aux déposants de ne retirer que 1 ether par semaine.
EtherStore.sol:```solidity contract EtherStore {
uint256 public withdrawalLimit = 1 ether;
mapping(address => uint256) public lastWithdrawTime;
mapping(address => uint256) public balances;
function depositFunds() public payable {
balances[msg.sender] += msg.value;
}