コントラクトのソースコードが公開されているからといって安全ではないという話
こちらのコードでmemberコントラクトのcallA関数が呼ばれた場合、イベントには何が出力されるでしょうか。Constructorの引数がContractAにキャストされ、ContractAのlogs関数が呼ばれる。Logsイベントがemitされますから、ログへの出力は”ContractA was called”。素直にコードを読めばそうなります。
code:main.sol
contract member {
ContractA _a;
constructor(address _sender) {
_a = ContractA(_sender);
}
function callA() public {
_a.log();
}
}
contract ContractA {
event Log(string message);
function log() public {
emit Log("ContractA was called");
}
}
code:another.sol
contract ContractB {
event Log(string message);
function log() public {
emit Log("ContractB was called");
}
}
実際のコードはこちらです。
まずこのコードのおかしなところは contractA(_sender); ですよね。
contractAには引数を持つコンストラクターなど存在しません。
つまり呼ばれても何も起きない処理が記述されているわけです。
何も行われない処理しかないmemberコントラクトのコンストラクターにコントラクトアドレスを渡すとどうなるか。エラーなどが発生するわけではなく、fallbackされて渡されたコントラクトの参照がそのまま_aに入ることになります。
では、memberコントラクトにいつcontractBへの参照を渡すか。deploy時に渡してしまいます。
まずcontractBをdeployして、そのアドレスを渡す形でmemberコントラクトをcontarctAとともにdeployします。
これで、memberコントラクトは contractBへの参照を引数としたコンストラクターが呼ばれながらインスタンス化されます。
そして先述の通りcontractA( contractBのアドレス) では何も処理はおきないわけですから、_aにはcontractBへの参照がそのまま渡されることになります。
_aはcontractBへの参照を保持しているわけですから_a.log()は contractBのlog()が呼ばれることになります。
問題は このcontractBへの参照が渡されているということを、公開されているソースからはパッと見で分からないということですよね。
表面的に見るとcontractBの存在には気づかない。contractAのlogが呼ばれるように感じてしまうわけですよね。
これを悪用すると、例えば 引き出しは depositしたアドレスからしか行えませんというコードを公開しつつ、 depositしたアドレス+特定のアドレスからなら引き出せるというロジックの方をパっと見分からないように適用させることが可能となります。
実際にはBが動いているわけですが、コードが公開されているAが正常に動いていると思わせ、資産がたまってきたところで、一気にBに設定されているアドレスを利用して引き出すという悪用も可能となります。
あれ?これfallbackするんじゃね?という処理が入っている場合には注意が必要ですね。
そして、公開されているソースではなく、deployの段階で別のコントラクトへの参照が渡されている可能性があるようなコードにも注意する必要があります。
こういうロジックがひっそりとフリーで提供されているライブラリーやフレームワークに分かりにくいように仕込まれていると、それを利用しているdappsすべてがリスクにさらされることになります。また、その時点では問題がなくても、ploxy delegateやupgradableを利用している場合は、任意のタイミングで悪意のあるコードに置き換えられてしまう可能性もあります。
暗号資産に絡むdappsは人の資産に影響を与えるものが多くなるわけですから、基本的にはすべて自分たちがコーディングし、処理を把握しているソースコードのみで構築されるべきです。もしライブラリーやフレームワークを使う場合にはそのコードをしっかりと検証しておかないと悪意のあるコードが含まれている場合は当然のことながら、バグや脆弱性によって自らのdapps、そして利用してくれている人たちをリスクにさらすことになりかねません。
安易なライブラリーやフレームワークの利用は確かに開発効率をあげる面はありますが、一定のリスクをかかえることを忘れてはいけないですよね。