# Hacks de smart contracts 2026 — Leçons et prévention
Le premier trimestre 2026 a vu plus de 500 millions de dollars volés dans des hacks de smart contracts. Malgré les progrès en sécurité, les vulnérabilités persistent. Voici les hacks majeurs, les patterns d'attaque, et comment protéger vos contrats.
Hacks majeurs du T1 2026
| Protocole | Perte | Type d'attaque | Date |
|-----------|-------|----------------|------|
| YieldVault Pro | $120M | Reentrancy + flashloan | 15 Jan 2026 |
| BridgeX | $95M | Signature replay | 3 Fév 2026 |
| StakeMax | $78M | Integer overflow (legacy code) | 18 Fév 2026 |
| DeFiSwap V3 | $65M | Price oracle manipulation | 5 Mar 2026 |
| LendProtocol | $42M | Access control bug | 12 Mar 2026 |
Total T1 2026 : $500M+ (source : Rekt News, CertiK)
Top 5 des vulnérabilités en 2026
1. Reentrancy (encore et toujours)
Malgré des années de sensibilisation, la reentrancy reste la vulnérabilité n°1.
Cas YieldVault Pro (120M$) :
// Contrat vulnérable
contract YieldVault {
mapping(address => uint256) public balances;
function withdraw() external {
uint256 amount = balances[msg.sender];
(bool success, ) = msg.sender.call{value: amount}(""); // ❌ REENTRANCY
require(success);
balances[msg.sender] = 0; // Trop tard
}
}
// Attaque
contract Attacker {
YieldVault vault;
uint256 count;
receive() external payable {
if (count < 10) {
count++;
vault.withdraw(); // Réentrance avant que balance = 0
}
}
function attack() external {
vault.withdraw();
}
}
Fix :
// ✅ Pattern Checks-Effects-Interactions
function withdraw() external {
uint256 amount = balances[msg.sender];
balances[msg.sender] = 0; // EFFECTS d'abord
(bool success, ) = msg.sender.call{value: amount}("");
require(success);
}
// Ou utilisez ReentrancyGuard d'OpenZeppelin
contract YieldVault is ReentrancyGuard {
function withdraw() external nonReentrant {
// Logique sécurisée
}
}
Mieux encore : utilisez le stockage transient (Solidity 0.8.28+)
bool private transient locked;
modifier nonReentrant() {
require(!locked, "Reentrant call");
locked = true;
_;
locked = false;
}
2. Manipulation d'oracle de prix
DeFiSwap V3 a perdu 65M$ à cause d'un oracle manipulable.
Vulnérabilité : Utilisation du prix spot d'un DEX comme oracle.
// ❌ Vulnérable
contract Lending {
function getPrice(address token) public view returns (uint256) {
(uint256 reserve0, uint256 reserve1,) = IUniswapPair(pair).getReserves();
return reserve1 * 1e18 / reserve0; // Prix spot manipulable avec un gros swap
}
}
Attaque :
Fix : utilisez un oracle TWAP (Time-Weighted Average Price)
// ✅ Oracle Uniswap V3 TWAP
contract SafeLending {
IUniswapV3Pool public pool;
uint32 public twapInterval = 1800; // 30 minutes
function getPrice() public view returns (uint256) {
uint32[] memory secondsAgos = new uint32[](2);
secondsAgos[0] = twapInterval;
secondsAgos[1] = 0;
(int56[] memory tickCumulatives, ) = pool.observe(secondsAgos);
int56 tickCumulativesDelta = tickCumulatives[1] - tickCumulatives[0];
int24 tick = int24(tickCumulativesDelta / int56(uint56(twapInterval)));
return OracleLibrary.getQuoteAtTick(tick, 1e18, token0, token1);
}
}
Encore mieux : utilisez Chainlink ou un agrégateur multi-sources
contract UltraSafeLending {
AggregatorV3Interface public chainlinkOracle;
IUniswapV3Pool public uniswapPool;
function getPrice() public view returns (uint256) {
uint256 chainlinkPrice = getChainlinkPrice();
uint256 uniswapPrice = getUniswapTWAP();
// Si l'écart > 2%, revert (manipulation détectée)
uint256 deviation = abs(chainlinkPrice - uniswapPrice) * 100 / chainlinkPrice;
require(deviation < 2, "Price deviation too high");
return (chainlinkPrice + uniswapPrice) / 2;
}
}
3. Signature replay attack
BridgeX a perdu 95M$ à cause de signatures réutilisables entre chaînes.
Vulnérabilité : Signature valide sur Ethereum réutilisée sur Polygon.
// ❌ Vulnérable
contract Bridge {
function withdraw(
uint256 amount,
bytes memory signature
) external {
bytes32 hash = keccak256(abi.encodePacked(amount, msg.sender));
address signer = recoverSigner(hash, signature);
require(signer == validator, "Invalid signature");
// Pas de nonce, pas de chainId → rejouable
payable(msg.sender).transfer(amount);
}
}
Fix : incluez nonce + chainId dans la signature
// ✅ Sécurisé
contract SecureBridge {
mapping(address => uint256) public nonces;
function withdraw(
uint256 amount,
uint256 nonce,
bytes memory signature
) external {
require(nonce == nonces[msg.sender], "Invalid nonce");
bytes32 hash = keccak256(abi.encodePacked(
amount,
msg.sender,
nonce,
block.chainid // Évite le replay cross-chain
));
address signer = recoverSigner(hash, signature);
require(signer == validator, "Invalid signature");
nonces[msg.sender]++;
payable(msg.sender).transfer(amount);
}
}
Encore mieux : utilisez EIP-712 pour les signatures structurées
bytes32 public DOMAIN_SEPARATOR = keccak256(abi.encode(
keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"),
keccak256(bytes("SecureBridge")),
keccak256(bytes("1")),
block.chainid,
address(this)
));
function withdraw(
uint256 amount,
uint256 nonce,
bytes memory signature
) external {
bytes32 structHash = keccak256(abi.encode(
keccak256("Withdraw(uint256 amount,address recipient,uint256 nonce)"),
amount,
msg.sender,
nonce
));
bytes32 digest = keccak256(abi.encodePacked("\x19\x01", DOMAIN_SEPARATOR, structHash));
address signer = recoverSigner(digest, signature);
require(signer == validator, "Invalid signature");
nonces[msg.sender]++;
payable(msg.sender).transfer(amount);
}
4. Integer overflow (code legacy)
StakeMax a perdu 78M$ avec du code Solidity 0.7.x non migré.
Vulnérabilité : Overflow non détecté avant Solidity 0.8.0.
// ❌ Solidity 0.7.6 (pas de protection overflow)
pragma solidity ^0.7.6;
contract StakeMax {
mapping(address => uint256) public rewards;
function claimRewards() external {
uint256 reward = rewards[msg.sender];
uint256 bonus = reward * 2; // Si reward > type(uint256).max / 2 → overflow
rewards[msg.sender] = 0;
payable(msg.sender).transfer(bonus); // Transfère le montant overflowé
}
}
Fix : migrez vers Solidity 0.8.0+
// ✅ Solidity 0.8.28 (overflow protection intégrée)
pragma solidity ^0.8.28;
contract SafeStakeMax {
mapping(address => uint256) public rewards;
function claimRewards() external {
uint256 reward = rewards[msg.sender];
uint256 bonus = reward * 2; // Revert automatiquement si overflow
rewards[msg.sender] = 0;
payable(msg.sender).transfer(bonus);
}
}
Si vous DEVEZ rester en 0.7.x, utilisez SafeMath
import "@openzeppelin/contracts/math/SafeMath.sol";
contract LegacyStakeMax {
using SafeMath for uint256;
function claimRewards() external {
uint256 reward = rewards[msg.sender];
uint256 bonus = reward.mul(2); // Revert si overflow
rewards[msg.sender] = 0;
payable(msg.sender).transfer(bonus);
}
}
5. Access control bugs
LendProtocol a perdu 42M$ à cause d'un modificateur mal configuré.
Vulnérabilité : Fonction admin sans restriction.
// ❌ Vulnérable
contract LendProtocol {
address public owner;
function setOwner(address newOwner) external {
owner = newOwner; // ❌ N'importe qui peut appeler
}
function withdrawFees() external {
require(msg.sender == owner);
// ...
}
}
Fix : utilisez OpenZeppelin Ownable
// ✅ Sécurisé
import "@openzeppelin/contracts/access/Ownable.sol";
contract SafeLendProtocol is Ownable {
function setOwner(address newOwner) external onlyOwner {
transferOwnership(newOwner);
}
function withdrawFees() external onlyOwner {
// ...
}
}
Encore mieux : utilisez AccessControl pour les rôles multiples
import "@openzeppelin/contracts/access/AccessControl.sol";
contract RobustLendProtocol is AccessControl {
bytes32 public constant ADMIN_ROLE = keccak256("ADMIN_ROLE");
bytes32 public constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE");
constructor() {
_grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
_grantRole(ADMIN_ROLE, msg.sender);
}
function withdrawFees() external onlyRole(ADMIN_ROLE) {
// Seuls les admins
}
function pauseProtocol() external onlyRole(OPERATOR_ROLE) {
// Les opérateurs peuvent pauser
}
}
Checklist de sécurité 2026
Avant le déploiement
- [ ] Reentrancy : Utilisez Checks-Effects-Interactions ou ReentrancyGuard
- [ ] Oracles : TWAP ou Chainlink, jamais de prix spot
- [ ] Signatures : Incluez nonce + chainId, utilisez EIP-712
- [ ] Overflow : Utilisez Solidity 0.8.0+ (ou SafeMath en 0.7.x)
- [ ] Access control : Utilisez Ownable ou AccessControl
- [ ] Integers : Évitez les divisions avant les multiplications
- [ ] Flashloans : Protégez contre les manipulations atomiques
- [ ] Frontrunning : Utilisez commit-reveal ou MEV protection
- [ ] Randomness : Utilisez Chainlink VRF, pas
block.timestamp
- [ ] Centralization : Multisig (2/3, 3/5) pour les fonctions admin
Tests
- [ ] Tests unitaires : 100% de couverture des fonctions critiques
- [ ] Tests de fuzz : Foundry invariant testing
- [ ] Tests d'intégration : Interactions entre contrats
- [ ] Simulation de mainnet fork
- [ ] Tests sur testnet (2-4 semaines minimum)
Audits
- [ ] Au moins 2 audits indépendants (CertiK, OpenZeppelin, Trail of Bits)
- [ ] Bug bounty (Immunefi, Code4rena)
- [ ] Timelock sur les upgrades (48h minimum)
- [ ] Multisig pour les fonctions sensibles
Post-déploiement
- [ ] Monitoring en temps réel (Tenderly, Forta)
- [ ] Circuit breakers (pause en cas d'anomalie)
- [ ] Assurance (Nexus Mutual, InsurAce)
- [ ] Plan de réponse aux incidents
- [ ] Communication transparente avec la communauté
Outils de sécurité recommandés
Analyse statique
- Slither : Détection automatique de 70+ vulnérabilités
- Mythril : Analyse symbolique
- Securify : Vérification formelle
# Installez Slither
pip3 install slither-analyzer
# Analysez votre contrat
slither Contract.sol
# Résultat : liste des vulnérabilités détectées
Tests de fuzz
- Foundry invariant testing : Teste des milliers de scénarios aléatoires
// test/Invariant.t.sol
contract InvariantTest is Test {
YourContract public contract;
function setUp() public {
contract = new YourContract();
}
// Invariant : le solde total ne peut jamais dépasser le supply
function invariant_totalBalanceNeverExceedsSupply() public {
uint256 totalBalance = contract.totalBalance();
uint256 supply = contract.totalSupply();
assertLe(totalBalance, supply);
}
}
forge test --invariant
Monitoring
- Tenderly : Alertes en temps réel, simulation de tx
- Forta : Bots de détection d'anomalies
- OpenZeppelin Defender : Automatisation et monitoring
Leçons apprises
Conclusion
Plus de 500M$ perdus au T1 2026, mais la majorité des hacks étaient évitables avec des pratiques de base. Reentrancy, oracles manipulables, et access control bugs dominent toujours. Utilisez les bons outils (Slither, Foundry, Chainlink), auditez par plusieurs firmes, et testez massivement avant de déployer.
Prochaine étape : Auditez vos contrats existants avec Slither et Foundry, puis planifiez un audit professionnel.
---
*Apprenez les vulnérabilités de sécurité avec des défis pratiques sur Solingo — maîtrisez Solidity en toute sécurité.*