スマートコントラクトのセキュリティ Part 2
ーSolidityの6つの脆弱性とその対策ー
本記事は、How to Secure Your Smart Contracts: 6 Solidity Vulnerabilities and how to avoid them (Part 2)(Georgios Konstantopoulos) の翻訳です。万一誤訳などありましたらPrivate Note機能でお知らせ下さい。
パート1で説明した脆弱性はより有名な、もしくは明らかなものであったが、この記事ではまだ広く悪用されていない脆弱性について説明していく。
導入をとばして、すぐ説明に入っていこう:
4. コントラクトへのEtherの強制送金
Solidityのselfdestruct
は二つの働きを持つ。
- 効果的にそのアドレスのバイトコードを削除することで、コントラクトを使えないようにする。
- コントラクトの全資産をターゲットアドレスに送る。
この特別なケースとして、もし受け取りアドレスがコントラクトであった場合、そのfallback関数は実行されない。
つまりコントラクトのある関数が、コントラクトの残高が一定量より少ないという条件ステートメントを持つ場合、そのステートメントはとばされる可能性がある:
fallback関数を投げることで、通常コントラクトはEtherを受け取ることができない。しかしあるコントラクトがこのコントラクトをターゲットとしてselfdestructした場合、このfallback関数は呼び出されない。その結果this.balance
は0より大きくなり、こうして攻撃者はonlyNonZeroBalance
におけるrequire
ステートメントをとばすことが可能となる。
対策: コントラクトの残高を使った防止策は絶対とらないこと!
5. 予期せぬrevertでのDoS
この脆弱性は King of the Etherのスマート・コントラクトに見られたものだ。
このケースではまず、安全性の低いコントラクトに対して、攻撃者のコントラクトが十分な額のEtherを送ればリーダーシップを宣言できてしまう。その後リーダーシップを取りたい別のプレーヤーのトランザクションは、上の25行目のコードでスローしてしまうのだ。
単純な攻撃だが、これはコントラクトが使用できなくなるまで、永久にサービス拒否をする原因となる。他のポンジ・スキームのコントラクトでも同じパターンの脆弱性が見られる。
6. 短いアドレスの攻撃
この攻撃はGolemチームによって発見され、こちらの記事にて説明された。攻撃者はこの脆弱性により、transfer
関数を悪用し許されるよりも多量のERC20トークンを引き出すことが可能となる。
注: 簡潔にするため、通常の半分のバイトサイズで表記する。
このバグを説明するため、こう考えてみよう。ある取引所のウォレットには10000 tokens
があり、あるユーザーの取引所のウォレット残高は32 tokens
である。またこのユーザーのアドレスは0x12345600
だとしよう。末尾にあるゼロ2つに注目してほしい。ユーザーは残高よりも多額を引き出そうと、取引所でトークンの引き出しボタンをクリックし、末尾のゼロ2つを省いてアドレスを入力する(この取引所は入力バリデーションを行っておらず、攻撃者のアドレスの長さは無効であるがトランザクション実行を許可してしまう)。
その後EVMはトランザクションの入力データを計算し、関数の署名と引数との連結によってトランザクションは実行される。
ERC20のtransfer関数はtransfer(address to, uint256 amount)
と定式化されている。3つのフィールドは次のようになる:
sig : a9059cbb = web3.sha3("transfer(address,uint256)").slice(0,10)
arg1: 123456 = receiving address
arg2: 00000020 = 32 in hexademical (0x20)
----------------------------------------
Concatenated: a9059cbb 123456 00000020
Transaction input data: 0xa9059cbb12345600000020
脆弱性
よく見ると、トランザクションの長さが2バイト(実際の完全な長さでは4バイト)短い。この場合、EVMはトランザクションの末尾にゼロを付け足し、このようになる:
0xa9059cbb1234560000002000// a9059cbb = web3.sha3("transfer(address,uint256)").slice(0,10)
// 12345600 = receiving address
// 00002000 = 8192 in hexademical (0x2000 == 0x20<<8)
こうすることで攻撃者の取引所の残高は32 tokens
であるにもかかわらず、それよりかなり大きい額の送金を完全に正当に実行できてしまう。 これはもちろん、送信元アカウント(取引所のウォレット)が送金に十分な残高を持つことに依存している。
対策:
- もし
msg.data
が無効なサイズであればスローすること。 - 取引所は入力バリデーションを行うこと。
おまけ:
常にコントラクトのビジネス・ロジックとしてnow
やblock.blockhash
を使用することは避けるように。なぜならその結果は予測可能であり、またマイナーによって操作可能であるからだ。こちらがより詳しい。
スマート・コントラクトをセキュアにするためには何をすれば良いのか?
これについては多くの場で述べられているが、私が個人的に気に入っているのはこちらだ。
その他だと、重要な点は:
- 凝ったコードを書かないこと。
- オーディット済み/テスト済みのコードを使用すること。
- できるだけたくさんユニット・テストを書くこと。
コードのオーディットや解析をするにはどんなツールがあるのか?
まず初めに、 solcのセマンティック・チェックでコンパイル時に潜在的なミスが発見されることは、セキュリティ対策への大きな一歩である。
- Securify.chは、スマート・コントラクトのための静的解析ツールだ。
- Remixもコードを静的に分解析し、初期化されていないストレージポインターやリエントラント性といったバグを見つけることができる。
- Oyenteは、最近発表されたスマート・コントラクトのための解析ツールである。
- Hydraは、「クリプトエコノミックなコントラクトのセキュリティ、そして分散型セキュリティのバウンティのためのフレームワーク」だ。
- Porosityは、「ブロックチェーンベースのイーサリアム・スマート・コントラクト用デコンパイラ」である。
- Manticore は、EVMをサポートする動的バイナリ解析ツールだ。
- Ethersplay はEVMのための Binary Ninja プラグインだ。
最後に、solgraphなどのツールを用いてコードを視覚化することは、関数の可視性について潜在的なバグを見つける手助けとなりうる。
注: Devcon3の2日目はコントラクトのセキュリティについての話で持ちきりだった。動画を見ておくことを絶対におすすめする[1,2]。
スマート・コントラクトに対する攻撃の詳細については、次を参照のこと:
- The Underhanded Solidity Contest [1,2,3]
- Trailofbits/not-so-smart-contracts
- A survey of attacks on Ethereum Smart Contracts
- Smart contract best practices — Known attacks
- Onward with Smart Contract Security
スマート・コントラクトのハッキング・スキルは以下で実践できる:
- Ethernaut (ropstenテストネットのアカウントが必要)
- HackThisContract (rinkebyテストネットのアカウントが必要)
もしSokidityやコードのセキュリティに関して質問があれば、Telegram(日本語)に参加するか、ブロックチェーンやイーサリアムに関するQ&AサイトDelegateCall(英語)に投稿してみましょう!DelegateCallはLoom Network初のDAppチェーン上で動くDAppです。
Loom Network は、イーサリアムのハイスケーラブルなDPoSサイドチェーン構築のためのプラットフォームで、大規模ゲームやソーシャルアプリにフォーカスしています。
さらなる情報は こちらから.
LOOMトークンをステークして、PlasmaChainのセキュリティ維持に参加しませんか? やり方はこちら
あなたがブロックチェーンゲームのファンであれば、 Zombie Battlegroundをチェック!世界初・独自のブロックチェーン上でフルに稼働するPC & モバイルカードゲームです。
そしてもしこの記事をお楽しみいただけ、最新情報の受け取りをご希望であれば、私たちの プライベートメーリングリストへの登録や、Telegram、Twitter、GithubやQiitaのフォローをお願いします!
Thanks to James Martin Duffy.