重入攻撃とは何ですか?
コントラクトが特定の関数を実行している間に、攻撃者がさまざまな方法でその関数を繰り返し呼び出すことができます。コントラクトの状態に依存があるため、繰り返し呼び出すことで予期しないロジックの破壊が起こります。例えば、コントラクトは送金前にユーザーの残高を減らし、送金後に受信者の残高を増やすといった処理を行っています。攻撃者は残高が減少するタイミングでコントラクトを繰り返し呼び出すことで、残高を複数回減らすことができます。
重入攻撃を防ぐ方法:
関数の実行前に状態変数を設定して重入を防ぐ。
外部コントラクトの呼び出しを避ける。安全な呼び出し方法をインターフェースで定義することができます。
ミューテックス(Mutex)を使用して同時呼び出しの競合を防ぐ。
トークンの approve 後に直接 transferFrom を呼び出すことを避け、approve の額を 2 つのステップの間でチェックする。
コード例:
// ミューテックスを使用して重入を防ぐ
Mutex mutex;
function withdraw(uint amount) external {
require(!mutex.locked());
mutex.lock();
msg.sender.call.value(amount)();
mutex.release();
}
// 状態変数を使用して重入を防ぐ
uint256 protecting;
function withdraw() external {
require(protecting == 0);
protecting = 1;
msg.sender.transfer(balance[msg.sender]);
protecting = 0;
}