**クイックバージョン**: 再入可能性は、あなたがまだお金を送金している最中にハッカーがあなたに電話をかけ直すようなもので、取引が終わる前にあなたのウォレットを空にされてしまいます。残酷な真実をお伝えします: $100M は再入可能性の脆弱性によって失われました。最も有名なものは? DAOハック(2016)は、この脆弱性を利用して$50M のETHを盗みました。## 攻撃の仕組み (実際のコードロジックを使用して)ContractAは10 ETHを保持し、ContractBはその内部に1 ETHの残高を持っていると想像してください。ContractBがwithdrawAll()を呼び出すと、次のことが起こるべきです:1.バランスを確認します>0 ✓2. ETHを送り返す ✓3.残高を0に更新します ✓しかし、ここで問題が発生します:攻撃者は**演算の順序**を利用します。**エクスプロイトフロー**:- 攻撃者は、ContractA で withdrawAll() を呼び出す attack() → を呼び出します- ContractAは1ETHを送信し、攻撃者のfallback()機能をトリガーします- 残高が 0 に更新される前に、fallback() はすぐにwithdrawAll()に再度電話をかけます- ContractAはチェックします: "残高は > 0 ですか?" はい (まだ更新されていないため!)- もう1 ETHを送信 → 再びfallback()をトリガーします- これはContractAが完全に drained されるまでループします**重要な洞察**: 残高の更新はETH転送の後に行われます。それが脆弱性のウィンドウです。## 3つの防衛戦略**1.非Reentrant修飾子(Single関数Protection)**実行中は機能をロックします。再入は許可されません:ソリディティ修飾子 nonReentrant {require(!locked, "再入禁止"); ロック = true; _;ロック = false;}シンプルですが、一度に一つの機能しか保護しません。**2. チェック・エフェクト・インタラクション パターン (マルチファンクション保護)**これはゲームチェンジャーです:- **チェック内容**:条件の確認(balance > 0)- **効果**: ステート (balance = 0) ← ETHを送信する前にこれを移動する- **インタラクション**: ETHを送信注文が間違っています:require(balance > 0);ETHを送る→→バランス= 0;遅すぎました!正しい順序:require(balance > 0);→ バランス = 0; // FIRSTを更新→ ETHを送信 // その後にインタラクト今、フォールバック()が再入場しても、残高はすでに0です。攻撃は失敗します。**3.GlobalReentrancyGuard (クロスコントラクトProtection)**複雑なシステムで複数の相互作用する契約がある場合は、すべての契約のロック状態を追跡する中央集権的なガード契約を使用します。ContractAがContractBを呼び出すと、ガードはそれを記録します。ContractBが戻る前にシステムに呼び戻そうとすると、ガードがそれをブロックします。## これが重要な理由再入可能性はSolidityの問題だけではなく、**設計の問題**です。ETHを送信したり外部関数を呼び出すたびに、あなたは信頼できないコードに制御を渡しています。攻撃者のフォールバック関数は、あなたのコントラクトのコンテキストで実行されます。**データ**: Chainalysisは、2023-2024年における高価値のエクスプロイトの約60%が再入可能性またはそれに類似したパターンに関与していることを発見しました。Yearn、Curve、Balancerなどの主要プロジェクトはすべて、再入可能性の危機に直面しました。**結論**: デフォルトでチェック-エフェクト-インタラクションを使用する。必要に応じてnonReentrantを追加する。マルチコントラクトシステムの場合は、GlobalReentrancyGuardを実装する。失われた$100M以上は、これらの基本的なパターンで防げたかもしれない。@TheBlockChainerをフォローして、Web3セキュリティの深堀り情報をもっと得よう。
再入攻撃: なぜスマートコントラクトが次々と drained ( されるのか、そしてそれを止める方法 )
クイックバージョン: 再入可能性は、あなたがまだお金を送金している最中にハッカーがあなたに電話をかけ直すようなもので、取引が終わる前にあなたのウォレットを空にされてしまいます。
残酷な真実をお伝えします: $100M は再入可能性の脆弱性によって失われました。最も有名なものは? DAOハック(2016)は、この脆弱性を利用して$50M のETHを盗みました。
攻撃の仕組み (実際のコードロジックを使用して)
ContractAは10 ETHを保持し、ContractBはその内部に1 ETHの残高を持っていると想像してください。
ContractBがwithdrawAll()を呼び出すと、次のことが起こるべきです: 1.バランスを確認します>0 ✓ 2. ETHを送り返す ✓ 3.残高を0に更新します ✓
しかし、ここで問題が発生します:攻撃者は演算の順序を利用します。
エクスプロイトフロー:
重要な洞察: 残高の更新はETH転送の後に行われます。それが脆弱性のウィンドウです。
3つの防衛戦略
1.非Reentrant修飾子(Single関数Protection)
実行中は機能をロックします。再入は許可されません: ソリディティ 修飾子 nonReentrant { require(!locked, “再入禁止”); ロック = true; _; ロック = false; }
シンプルですが、一度に一つの機能しか保護しません。
2. チェック・エフェクト・インタラクション パターン (マルチファンクション保護)
これはゲームチェンジャーです:
注文が間違っています:
require(balance > 0); ETHを送る→ →バランス= 0;遅すぎました!
正しい順序:
require(balance > 0); → バランス = 0; // FIRSTを更新 → ETHを送信 // その後にインタラクト
今、フォールバック()が再入場しても、残高はすでに0です。攻撃は失敗します。
3.GlobalReentrancyGuard (クロスコントラクトProtection)
複雑なシステムで複数の相互作用する契約がある場合は、すべての契約のロック状態を追跡する中央集権的なガード契約を使用します。ContractAがContractBを呼び出すと、ガードはそれを記録します。ContractBが戻る前にシステムに呼び戻そうとすると、ガードがそれをブロックします。
これが重要な理由
再入可能性はSolidityの問題だけではなく、設計の問題です。ETHを送信したり外部関数を呼び出すたびに、あなたは信頼できないコードに制御を渡しています。攻撃者のフォールバック関数は、あなたのコントラクトのコンテキストで実行されます。
データ: Chainalysisは、2023-2024年における高価値のエクスプロイトの約60%が再入可能性またはそれに類似したパターンに関与していることを発見しました。Yearn、Curve、Balancerなどの主要プロジェクトはすべて、再入可能性の危機に直面しました。
結論: デフォルトでチェック-エフェクト-インタラクションを使用する。必要に応じてnonReentrantを追加する。マルチコントラクトシステムの場合は、GlobalReentrancyGuardを実装する。失われた$100M以上は、これらの基本的なパターンで防げたかもしれない。
@TheBlockChainerをフォローして、Web3セキュリティの深堀り情報をもっと得よう。