This page may contain third-party content, which is provided for information purposes only (not representations/warranties) and should not be considered as an endorsement of its views by Gate, nor as financial or professional advice. See Disclaimer for details.
Solidityコンパイラの脆弱性解析と対策
Solidityコンパイラの脆弱性解説と対策
コンパイラは現代のコンピュータシステムの基本的な構成要素の一つです。これはコンピュータプログラムであり、主な機能は高級プログラミング言語のソースコードをコンピュータの低レベルCPUまたは仮想マシンが実行可能な命令コードに変換することです。
ほとんどの開発者やセキュリティ専門家は通常、アプリケーションコードのセキュリティに関心を持っていますが、コンパイラ自体のセキュリティも同様に重要です。コンピュータプログラムとして、コンパイラにもセキュリティの脆弱性が存在する可能性があり、場合によっては深刻なセキュリティリスクを引き起こすことがあります。たとえば、ブラウザがJavaScriptフロントエンドコードをコンパイルおよび解析して実行する際、JavaScript解析エンジンの脆弱性により、ユーザーが悪意のあるウェブサイトにアクセスすると攻撃者に利用され、最終的に被害者のブラウザやさらにはオペレーティングシステムを制御される可能性があります。
Solidityコンパイラにもセキュリティの脆弱性があります。Solidity開発チームのセキュリティ警告によると、複数の異なるバージョンのSolidityコンパイラにセキュリティの問題が発見されました。
Solidityコンパイラの脆弱性
Solidityコンパイラの主な役割は、スマートコントラクトコードをEthereum仮想マシン(EVM)命令コードに変換することです。これらのEVM命令コードは、トランザクションによってパッケージ化されEthereumにアップロードされ、最終的にEVMによって解析され実行されます。
注意が必要なのは、Solidityコンパイラの脆弱性とEVM自体の脆弱性は異なるということです。EVMの脆弱性は、仮想マシンが命令を実行する際に発生するセキュリティ問題を指します。攻撃者が任意のコードをEthereumにアップロードできるため、EVMにセキュリティ脆弱性が存在する場合、Ethereumネットワーク全体に影響を及ぼし、サービス拒否(DoS)や、攻撃者によってブロックチェーン全体が乗っ取られる可能性があります。しかし、EVMの設計は比較的シンプルで、コアコードの更新は頻繁ではないため、このような問題が発生する可能性は低いです。
Solidityコンパイラの脆弱性とは、コンパイラがSolidityコードをEVMコードに変換する際に存在する問題を指します。ブラウザがユーザーのクライアントでJavaScriptをコンパイルして実行する場合とは異なり、Solidityのコンパイルプロセスはスマートコントラクト開発者のコンピュータ上でのみ行われ、イーサリアム上では実行されません。したがって、Solidityコンパイラの脆弱性はイーサリアムネットワーク自体には直接的な影響を与えません。
Solidityコンパイラの脆弱性の主要な危害の一つは、生成されたEVMコードが開発者の期待と一致しない可能性があることです。Ethereum上のスマートコントラクトは通常、ユーザーの暗号通貨資産を含むため、コンパイラによって引き起こされる契約のバグはユーザー資産の損失を引き起こす可能性があり、その結果は深刻です。
開発者と契約監査者は、主に契約コードのロジック実装問題、および再入、整数オーバーフローなどのSolidityレベルのセキュリティ問題に注目するかもしれません。しかし、コンパイラの脆弱性は、単に契約ソースコードを監査するだけでは発見が難しいことが多いです。特定のコンパイラバージョンと特定のコードパターンを組み合わせて分析する必要があり、スマートコントラクトがコンパイラの脆弱性の影響を受けているかどうかを特定する必要があります。
! 【Solidityコンパイラの脆弱性解析と対策】(https://img-cdn.gateio.im/webp-social/moments-7d1e882c0b106528437910218bf21f82.webp)
Solidityコンパイラの脆弱性の例
以下は、いくつかの実際のSolidityコンパイラの脆弱性の例であり、それらの具体的な形態、原因、及び危害を示しています。
SOL-2016-9 高次バイトクリーンストレージ
この脆弱性は、初期のSolidityコンパイラバージョンに存在します(>=0.1.6 <0.4.4)。
以下のコードを考慮してください:
ソリディティ コントラクトC { uint32 a = 0x12345678; uint32 b = 0; 関数 f() public { a = a + 1; } パブリック ビュー run()関数は (uint) { を返します。 bを返す; } }
storage変数bは何の変更も受けていないため、run()関数はデフォルト値の0を返すべきです。しかし、脆弱性のあるバージョンのコンパイラが生成したコードでは、run()は1を返します。
このコンパイラの脆弱性を理解していない限り、一般の開発者は簡単なコードレビューを通じてこのバグを発見するのは難しいです。この例は比較的簡単で、特に深刻な結果をもたらすことはありませんが、b変数が権限検証や資産記帳などの目的で使用されている場合、予期しない不一致は非常に深刻な結果を引き起こす可能性があります。
この現象の原因は、EVMがスタック型仮想マシンを使用しているためであり、スタック内の各要素は32バイトのサイズ(、すなわちuint256変数のサイズ)です。一方で、基盤ストレージであるstorageの各スロットも32バイトのサイズです。また、Solidity言語はuint32など32バイト未満のデータ型をサポートしており、コンパイラはこれらの型を処理する際に、その上位ビットを適切にクリアする操作(clean up)が必要です。上記の状況において、加算が整数オーバーフローを引き起こすと、コンパイラは結果の上位ビットを正しくclean upせず、オーバーフロー後の上位ビットの1がstorageに書き込まれ、最終的にa変数の後ろにあるb変数を上書きし、b変数の値が1に変更されてしまいます。
SOL-2022-4 インラインアセンブリメモリ副作用
この脆弱性は、>=0.8.13 <0.8.15バージョンのコンパイラに存在します。
以下のコードを考慮してください:
ソリディティ コントラクトC { function f() public pure は (uint) { を返します。 アセンブリ { mstore(0, 0x42) } uint x; アセンブリ { x := mload(0) } xを返す; } }
Solidityコンパイラーは、Solidity言語をEVMコードに変換する過程で、単純な翻訳だけでなく、制御フローとデータ分析を深く行い、さまざまなコンパイル最適化を実現して生成コードのサイズを縮小し、実行過程でのガス消費を最適化します。この種の最適化は、さまざまな高級言語のコンパイラーにおいて非常に一般的ですが、考慮すべき状況が複雑であるため、バグやセキュリティの脆弱性が発生しやすいです。
上記のコードの脆弱性は、この種の最適化操作に起因しています。もしある関数内にメモリの0オフセットのデータを変更するコードが存在し、その後にそのデータが使用されない場合、メモリ0を変更するコードを直接削除することができ、ガスを節約でき、後続のプログラムロジックには影響を与えません。
この最適化戦略自体には問題はありませんが、具体的なSolidityコンパイラーの実装では、この種の最適化は単一のassembly blockにのみ適用されます。上記のPoCコードでは、メモリ0の書き込みとアクセスが2つの異なるassembly blockに存在し、コンパイラーは個別のassembly blockのみを分析して最適化しました。最初のassembly blockでメモリ0に書き込んだ後に読み取り操作がないため、その書き込み命令は冗長であると判断され、削除されることになります。これが原因でバグが発生します。脆弱なバージョンではf(関数が返す値は0ですが、実際には正しい返り値は0x42であるべきです。
) SOL-2022-6 AbiReencodingHeadOverflowWithStaticArrayCleanup
この脆弱性は、バージョン >= 0.5.8 < 0.8.16 のコンパイラに影響します。
以下のコードを考慮してください:
ソリディティ コントラクトC { function f###string( calldata a[1] public pure は )string memory( { を返します。 abi.decode)abi.encode(a(、 )string([1])); } }
通常、上記のコードが返すa変数は"aaaa"であるべきです。しかし、脆弱性のあるバージョンでは空の文字列""が返されます。
この脆弱性の原因は、Solidityがcalldata型の配列に対してabi.encode操作を行う際に、誤っていくつかのデータをクリーンアップしてしまい、隣接する他のデータが変更されることによって、エンコードとデコード後のデータが不一致になったことです。
注意すべきは、Solidityがexternal callやemit eventを行う際に、暗黙的にパラメータをabi.encodeするため、上記の脆弱性コードが発生する確率は直感的に感じるよりも高くなるということです。
! 【Solidityコンパイラの脆弱性解析と対策】[0]https://img-cdn.gateio.im/webp-social/moments-c97428f89ed62d5ad8551cdb2ba30867.webp(
セキュリティに関する提案
Solidityコンパイラの脆弱性に対する脅威について、開発者およびセキュリティ専門家に次の提案をします:
開発者向け:
より新しいバージョンのSolidityコンパイラを使用してください。新しいバージョンは新たなセキュリティ問題を引き起こす可能性がありますが、既知のセキュリティ問題は通常、古いバージョンよりも少ないです。
ユニットテストケースを充実させる。ほとんどのコンパイラー関連のバグは、コードの実行結果が期待と一致しなくなる原因となる。これらの問題はコードレビューでは見つけにくいが、テスト段階で明らかになることが多い。コードカバレッジを向上させることで、このような問題を最大限に回避できる。
インラインアセンブリ、マルチディメンショナル配列や複雑な構造体のABIエンコーディング/デコーディングなどの複雑な操作の使用はできるだけ避け、明確な要件がない場合は言語の新機能や実験的機能を盲目的に使用しないようにしましょう。歴史的な脆弱性の大部分は、インラインアセンブリやABIエンコーダーなどの操作に関連しています。コンパイラは複雑な言語機能を処理する際にバグが発生しやすくなります。一方で、開発者は新機能を使用する際に誤った使い方をすることが多く、安全上の問題を引き起こす可能性があります。
安全スタッフへ:
Solidityコードのセキュリティ監査を行う際には、コンパイラが引き起こす可能性のあるセキュリティリスクを無視しないでください。Smart Contract Weakness Classification)SWC(における対応するチェック項目はSWC-102: 古いコンパイラバージョンです。
内部SDL開発プロセスにおいて、開発チームにSolidityコンパイラのバージョンをアップグレードするよう促し、CI/CDプロセスにおいてコンパイラバージョンの自動チェックを導入することを検討できます。
しかし、コンパイラの脆弱性について過度に恐れる必要はありません。ほとんどのコンパイラの脆弱性は特定のコードパターンでのみトリガーされ、脆弱性のあるバージョンのコンパイラでコンパイルされたコントラクトが必ずしも安全リスクを伴うわけではありません。実際の安全への影響はプロジェクトの状況に応じて具体的に評価する必要があります。
いくつかの実用的なリソース:
! 【Solidityコンパイラの脆弱性解析と対策】)https://img-cdn.gateio.im/webp-social/moments-84f5083d8748f2aab71fd92671d999a7.webp(
サマリー
この記事では、コンパイラの基本概念から出発し、Solidityコンパイラの脆弱性について紹介し、実際のEthereum開発環境で引き起こされる可能性のあるセキュリティリスクを分析し、開発者やセキュリティ専門家にいくつかの実用的なセキュリティ提案を提供します。コンパイラの脆弱性を理解し、重視することで、スマートコントラクトの安全性をより包括的に保証することができます。