界: Code Quality

コードの質が低いと、予測できない動作につながります。ユーザーの視点には、それがしばしば使い勝手の悪さとなって現れます。攻撃者にとっては、予期せぬ方法でシステムにストレスを与える機会となります。

Code Correctness: Reentrancy

Abstract
関数が Checks-Effects-Interaction パターンを壊す、またはリエントランシー攻撃からの保護に失敗します。
Explanation
リエントランシー攻撃は、最初の呼び出しが終了する前に、悪意のあるコントラクトが呼び出し元コントラクトにコールバックするときに発生します。これは、脆弱なコントラクトが、現在の呼び出しのエフェクトをローカルで実行する前に、外部コントラクトとインタラクションを行う関数を公開するときに発生します。これにより、外部コントラクトがインタラクションの制御フローを引き継ぐ可能性があります。

悪意のあるコントラクトが、呼び出し元コントラクトと安全でない方法で対話している被害者の公開済み関数を呼び出すと、攻撃者のコントラクトはそのインタラクションを (たとえばフォールバック関数経由で) 受信して処理し、call-interact-call ループに入るためにすぐに被害者の公開済み関数をコールバックします。この再帰的な状態になると、被害者側ではそれ以上コードを実行できなくなり、その結果、インタラクションの性質に応じて資産や価値が部分的または全体的に流出する可能性があります。

Checks-Effects-Interaction パターンは、誤ったロジックを防ぐためにスマート コントラクトでよく使用されます。このパターンは、コードがまず必要な条件をチェックし、続いて関連する状態変更を実行し (エフェクト)、最後にそのエフェクトに関連して外部コントラクトとのインタラクションを行うことを指定しています。

例 1: 次の例は、Checks-Effects-Interaction パターンに従わずに、チェック、インタラクション、エフェクトを実行することにより、リエントランシー攻撃を許可しています。

コード:

1.送信者の残高を確認します (チェック)。
2.msg.sender.call.value 経由で呼び出し元にイーサを送信します (インタラクション)。
3.送信者の残高を減らすことで状態変化を行います (エフェクト)。


function withdraw(uint amount) public{
if (credit[msg.sender] >= amount) {
require(msg.sender.call.value(amount)());
credit[msg.sender]-=amount;
}
}
Example 1 のコードは、Checks-Interaction-Effects を実行するのではなく、Checks-Effects-Interaction パターンを壊しています。これは、攻撃するスマート コントラクトがフォールバック関数でイーサを受け取り、すぐに withdraw をコールバックした場合に、リエントランシーにつながるおそれがあります。これにより、残高を減らすコード行 (エフェクト) が実行されないためにイーサが流出するという再帰的な状況が作られます。
References
[1] Enterprise Ethereum Alliance External Calls and Re-entrancy
[2] Standards Mapping - CIS Azure Kubernetes Service Benchmark 4
[3] Standards Mapping - CIS Amazon Elastic Kubernetes Service Benchmark 4
[4] Standards Mapping - CIS Amazon Web Services Foundations Benchmark 3
[5] Standards Mapping - CIS Google Kubernetes Engine Benchmark normal
[6] Standards Mapping - Common Weakness Enumeration CWE ID 841
[7] Standards Mapping - Smart Contract Weakness Classification SWC-107
desc.structural.solidity.swc107