重入攻撃とは何ですか?
ある契約が特定の関数を実行しているとき、攻撃者はさまざまな方法でその関数を繰り返し呼び出すことができます。契約の状態に依存があるため、繰り返し呼び出すことで予期しないロジックの破壊が起こります。例えば、契約は転送前にユーザーの残高を減らし、転送後に受信者の残高を増やします。攻撃者は残高が減少する際に契約を繰り返し呼び出すことで、残高を複数回減らすことができます。
重入攻撃を防ぐ方法:
関数の実行前に状態変数を設定して重入を防ぐ。
外部契約の呼び出しを避ける。安全な呼び出し方法をインターフェースで定義することができます。
Mutex(相互排他ロック)を使用して同時呼び出しの競合を防ぐ。
トークンの approve 後に直接 transferFrom を呼び出さず、approve の額をチェックする。
コード例:
// 重入を防ぐためにMutexを使用する
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;
}