スマートコントラクトに対するセキュリティ・ガイドライン
Solidityスマートコントラクトセキュリティ
中級以下に挙げる主な推奨事項を遵守することで、よりセキュアなスマートコントラクト開発が実現します。
設計ガイドライン
まず、コードを書く前に、コントラクトの設計について十分に議論を深める必要があります。
ドキュメンテーションおよび使用
ドキュメンテーションは様々な水準で作成でき、コントラクトの実装時に内容を修正する必要があります。
- 平易な英語で書かれたシステムの説明では、コントラクトが実行する内容を説明し、コードベースの前提条件を記述します。
- スキーマとアーキテクチャのダイアグラムでは、コントラクトで実行されるやりとりや、システムの状態マシンについて記述します。 Slitherのプリンター機能(opens in a new tab)は、スキーマを生成する上で有益です。
- コード内容を徹底的に文書化する。 Solidityの場合、Natspecフォーマット(opens in a new tab)を使用できます。
オンチェーン処理とオフチェーン処理の比較
- できるだけ多くのコードを、オフチェーンで処理するようにします。オンチェーンのレイヤーに含まれるコードは、小規模になるようにしてください。 オンチェーンでの検証がシンプルに実行できるように、オフチェーンのコードでデータを前処理します。 順序付きリストが必要か確認します。 必要な場合は、オフチェーンでリストをソートしてから、オンチェーンでは順序のみをチェックするようにします。
アップグレード可能性
アップグレードに関する様々なソリューションについては、こちらのブログ投稿(opens in a new tab)で検討しています。 コード開発を開始する事前に、アップグレードに対応するか否かをはっきり決定してください。 この決定により、どのような構造のコードを開発するべきかが変わってきます。 一般論として、以下を推奨します:
- アップグレード可能性よりも、コントラクトのマイグレーション(opens in a new tab)を優先すること。システムのマイグレーションは、アップグレード可能性が提供する利点の多くを有し、アップグレード可能性の欠点がありません。
- 委任呼び出しプロキシのパターンよりも、データ分離のパターンを使用すること。あなたのプロジェクトに明確な抽象化による分離が含まれる場合、データ分離をわずかに修正するだけでアップグレードが可能になります。 委任呼び出しのプロキシ は、EVMの知識が必要であり、エラー発生の頻度が高まります。
- デプロイする前に、マイグレーション/アップグレードの手順を文書化すること。ガイドラインが設定されていない場合、ストレスを受けながら作業する必要があるため、ミスを犯しやすくなります。 遵守すべき手順を前もって策定しておきましょう。 具体的には、以下を定めます:
- 新たなコントラクトを開始する呼び出し。
- キーの保存場所と、アクセス方法。
- デプロイの確認方法。 デプロイ後のスクリプトを開発、テストしておきます。
実装に関するガイドライン
シンプルさを追求する。常に、目的を達成する上で最もシンプルなソリューションを使用してください。 あなたのソリューションは、チーム全員が理解できるものでなければなりません。
関数の構成
コードベースのアーキテクチャは、レビューしやすいものでなければなりません。 コードの正確性を判定しにくくするようなアーキテクチャ関連の決定は避けてください。
- システムのロジックを分割する。複数のコントラクトに分割するか、類似の関数(認証、算術塩酸など)をグループ化しましょう。
- 明確な目的を持つ小規模の関数を書く。レビュー作業が容易になり、コンポーネントごとのテストが可能になります。
継承
- 継承を管理可能な水準に抑える。ロジックを分割するために継承を活用すべきですが、プロジェクト全体における継承ツリーの深さと広さをなるべく小さくするように努めてください。
- Slitherの継承プリンター(opens in a new tab)でコントラクトの階層を確認する。継承プリンターは、階層の規模を確認する上で役立ちます。
イベント
- すべての重要操作をロギングする。イベントログは、開発時におけるコントラクトのデバッグやデプロイ後の監視に役立ちます。
既知の落とし穴を回避する
- 最も一般的なセキュリティの問題に注意します。よくある問題点については、Ethernaut CTF(opens in a new tab)、Capture the Ether(opens in a new tab)、Not so smart contracts(opens in a new tab)などの様々なオンラインリソースを活用してください。
- Solidityドキュメント(opens in a new tab)の警告セクションに注意する。警告セクションを通じて、言語における把握しにくい振る舞いを把握することができます。
依存関係
- 実証済みのライブラリを使用する。実証済みのライブラリからコードをインポートすることで、バグが多いコードを書く可能性が低くなります。 ERC-20コントラクトを作成する場合は、 OpenZeppelin(opens in a new tab)を使用してください。
- 依存性マネージャーを使用し、コードのコピーアンドペーストを避ける。外部ソースに依存する場合は、元ソースに基づき最新状態を保つ必要があります。
テストと検証
- 完全な単体テストを作成する。質の高いソフトウェアを開発するには、包括的なテスト一式が不可欠です。
- Slither(opens in a new tab)、Echidna(opens in a new tab)、およびManticore(opens in a new tab)用のカスタムチェックとプロパティを作成する。自動化ツールを通じて、コントラクトのセキュリティを高めることができます。 効率的なチェックおよびプロパティを作成するには、本ガイドの該当記事を参照してください。
- crytic.io(opens in a new tab)を使用する。Cryticは、GitHubと統合されており、Slitherのプライベート検出器にアクセスできる他、Echidnaからカスタムのプロパティチェックを実行することができます。
Solidity
- Solidityのバージョンは、0.4や0.6ではなく0.5を使用する。現在のところ、Solidity 0.5が最もセキュアなバージョンであり、0.4よりもビルトインプラクティスが優れています。 一方、0.6は現時点において本番環境の使用に耐えうる安定性を持たず、さらなるアップデートが必要な状態です。
- 安定したバージョンでコンパイルし、最新リリースで警告をチェックする。最新バージョンのコンパイラを使って、コードの問題が報告されないことを確認してください。 ただし、Solidityはリリース間隔が短く、過去においてもコンパイラにバグが含まれていた場合が多いため、デプロイに最新バージョンを用いることは推奨しません(Slitherの推奨solcバージョン(opens in a new tab)を参照)。
- インラインアセンブラを使用しない。アセンブラを使用するには、EVMの専門知識が必要です。 イエローペーパーをマスターしていない場合、EVMコードを書かないでください。
デプロイに関するガイドライン
コントラクトの開発が完了し、デプロイしたら、以下を確認します:
- コントラクトの動作を監視する。ログを監視し、コントラクトやウォレットが不正利用された場合はただちに対応できる状態を保ってください。
- あなたの連絡先情報をblockchain-security-contacts(opens in a new tab)に追加する。このリストに登録することで、プロジェクトにおけるセキュリティ上の欠陥が発見された場合に、サードパーティから連絡を受けやすくなります。
- 特権ユーザーのウォレットを保護する。キーをハードウェアウォレットに保管する場合は、このベストプラクティス(opens in a new tab)に従ってください。
- 事故対応計画を策定する。スマートコントラクトは、不正利用される可能性があります。 開発したコントラクトにバグが含まれていない場合でも、攻撃者はコントラクト所有者の鍵を悪用しようとする可能性があります。
p
最終編集者: @pettinarip(opens in a new tab), 2023年12月8日