【Ethereum】スマートコントラクトのベストプラクティスについてまとめる②
前回分はこちら
pao2.hatenablog.com
続きを書きます。
既知の攻撃方法
Race Conditions
最も有名な攻撃の一つ。
DAO事件もこれ。
いくつかのパターンがあるのでそれを紹介する。
Reentrancy
メソッドの最初の呼び出しが終了する前に、fallback関数により、繰り返し呼び出されることで、何度も何度も残高から引き出されたりする。
対処法としては、
someAddress.call.value()()
ではなく、someAddress.send()
を使う- 内部の状態変数の更新を終えてから、外部アドレスを呼び出す
といった感じ。
前者は利用できるGASを制限、後者は繰り返し処理ができないように先に状態を変えておくといった対策。
Cross-function Race Conditions
1つのコントラクトに2つのメソッドがあり、1つのメソッドを実行したときに、fallback関数からもう一方のメソッドを実行する。
すると、一つ目のメソッドが実行途中で、状態更新が終わっていないタイミングにもう一方のメソッドが実行され、不整合が生じることがある。
外部コントラクトを呼び出す前に、内部の状態更新を行っておくのが良い。
これは、直接コード(Cross-function Race Conditionsの部分)を見た方がいいかもしれない。
Pitfalls in Race Condition Solutions
あるメソッドが直接外部コントラクトの呼び出しを行わなくても、他のメソッドを呼び出し、そのメソッドが外部コントラクトを呼び出していたら結局危ないよということ。
Transaction-Ordering Dependence (TOD) / Front Running
ブロック内のトランザクションの順番はマイナーにより意図的に操作でき、それによる攻撃の類。
Timestamp Dependence
ブロックのTimestampはマイナーにより操作可能であり、それを用いた攻撃。
例えば、乱数生成のシードにTimestampを使っていたとしたら、マイナーは乱数を調整できることになる。
Integer Overflow and Underflow
整数型のオーバーフローやアンダーフローにより、変数に予期せぬ値が入りそこを攻撃されることがある。
以前書いた、ERC20トークンでのBatchOverflowもこの攻撃。
DoS with (Unexpected) revert
常に失敗するfallback関数を用意することで、コントラクト自体が機能できなくなるような方法。
例えば、オークションの実装で、入札金額が更新されたとき前の最高金額者に金を戻す必要がある。
このとき前の最高金額者のコントラクトアドレスでfallback関数が失敗するようになっていたら、必ず更新が失敗し、誰も入札できなくなる。
こういったときは、pull型で入札者からお金を取りに来てもらうようにしたほうがいい。
DoS with Block Gas Limit
これは、GAS上限を狙った攻撃。
コントラクトに繰り返し処理などをさせることでGASぎれを狙う。
例えば、クラウドファンディングで寄付を募るコントラクトを作ったときに、寄付したアドレス一つずつに返金する処理をしたとする。
このとき悪意ある人が、少額を繰り返し違うアドレスで募金していたら、返金処理のときに繰り返し処理になりGASぎれが発生して途中で処理を失敗させられる。
Forcibly Sending Ether to a Contract
selfdestruct
を使うことで、fallback関数を呼び出すことなくコントラクトにEtherを送ることが可能。
これを利用してEtherをコントラクトに強制的に送金して、予期しない動作をさせることが可能になる。
ソフトウェアエンジニアテクニック
スマートコントラクトで開発する上でのテクニック。
Upgrading Broken Contracts
コントラクトに不具合が見つかったときに、別のコントラクトを最新のものとしてアップグレード可能にする方法。
非常に重要。
方法1:登録かつ最新バージョンを管理するコントラクトを用意する
管理者のみから登録できるようにし、歴代のコントラクトアドレスを保持しておく。
欠点としては以下がある。
- ユーザが最新のアドレスかどうかチェックしておく必要があり、古いバージョンにアクセスするユーザがいるリスクがある
- 古いコントラクトからデータをどう引き継ぐかを考える必要がある。
方法2:最新のコントラクトにデータを転送する
一つ目の方法の課題であるデータの引き継ぎを解消するために、delegatecall
を使う。
delegatecall
によってコントラクト間のデータの引き渡しが可能になる。
ただし、新しいコントラクトのデータの格納形式が違うと保存できないので注意。
このあたりは非常に重要なので別途勉強してまとめたい。
Circuit Breakers (Pause contract functionality)
コントラクトに不具合が見つかったときに、緊急停止できるようにする方法。
こちらも非常に重要。
コントラクト内の資金を停止と同時にどこか信頼できるアドレスに脱出させる。
サーキットブレーカー自体は、スマートコントラクト・ブロックチェーン に限らず使われているものである。
Speed Bumps (Delay contract actions)
処理を遅らせることで、コントラクトに不具合があって攻撃された場合でも被害を最小限に止めることができる。
例えば、資金の引き出しを10日後や1000ブロック後などとすることで、攻撃されていることに気づいて対処する時間を用意できる。
リアルタイム性が求められない処理であれば利用すればいいと思う。
Rate Limiting
処理できる量を制限する。
例えば、1日100ETHまでしか送金できない等の制限により、不正に大量の送金をすることを防ぐことができる。
Contract Rollout
メインネットでサービスリリースする前に、テストでコントラクトを動かすはずだが、その際の注意・したほうがいいこと。
- 100%でテストを行うこと
- まず自分のプライベートネットにてテストすること
- パブリックなテストネットにてテストとBug Bountyをすること
- テスト時には様々なプレイヤーがそのコントラクトにアクセスできること
- メインネットにも制限付きでベータ版をデプロイする
(特定のブロック番号を超えたらdestructする、1アカウントあたりの扱えるETHを制限するなど)
トークン実装におけるベストプラクティス
最新の標準トークンを用いる
ERC20
やERC721
などが有名ですが、実装する時点での最新の標準トークンを調べるのがよい
ERC20における「front running attacks」に気をつける
ERC20におけるapprove
とtransferFrom
メソッドにより「front running attack」という種類の攻撃が可能になる。
「front running attack」とは、上にも書いたが、トランザクションがブロックに書き込まれるまでの時間を利用した攻撃の種類である。
ここで発生しうる攻撃は以下のようなもの。
例えば、アリスがボブに10ETH分、アリスの口座から引き出すことを許可していて(approve
により)、途中でそれを5ETHに変えようとした場合、
5ETHに変えるapprove
のトランザクションがブロックに書き込まれる前にボブがそれに気づき、10ETH引き出すtransferFrom
を先に実行できるようにする。
すると、ボブは10ETHまず引き出すことが可能で、さらに5ETHに変えるapprove
のあとに5ETHも引き出すことが可能になる。
対策も含めて、こちらに記載されている。
0x0
のアドレスに送信するのを防ぐ
なんと誤送信だけで0のアドレス(0x000・・・・00
)に現在、 8000万ドルものETHが眠っているらしい。。。
そしてそのアドレスは、特に誰のものでもなく引き出せない。
また別ではあるが自身のコントラクトへの送金も含めて以下のようなmodifier修飾子で防ぐことができる
modifier validDestination( address to ) { require(to != address(0x0)); require(to != address(this) ); _; }
ドキュメント化について
以下の内容をドキュメント化して公開するべき。 - 詳細な仕組みやロールアウトに関する方針 - コンパイラのバージョンや、どのソースコードをデプロイしているかといった状態 - よくあるバグ・攻撃への対策・リスク状況 - 更新履歴 - 不具合が発生した時のアクションプラン - 問い合わせ先
セキュリティツールについて
セキュアなスマートコントラクトを書くために必要なツールの紹介。
コードに脆弱性が含まれていないかのチェックであったり、コードを綺麗にするためのリンターなど。
具体的なリンクが羅列されているので、以下を参照
- 日本語版
- 英語版
セキュリティ/脆弱性情報の発信・通知
どこからセキュリティや脆弱性などの情報が発信されてウォッチしておくべきか?といったこと。
具体的には、Ethereumの公式ブログやVitalikのTwitterなど
ここも詳しくは以下を参照。
- 日本語版
- 英語版
以上でベストプラクティスのまとめ終了。
長かった。。。。
まとめてみてわかったのは、
- 実際にコードを見ないと、どんな脆弱性があり対策をすべきなのか分からないものも多数ある
- ここに書いてあるのはあくまで書いた時点のものであり、これから気をつけるべきことはどんどん増えていく
- ブロックチェーン やスマートコントラクト特有の不具合や気をつけるべき点はかなり多い
といったこと。
GASの消費量とかも気にしてしまうけど、まずは脆弱性のない安全なコードを書けるようになることが、まず何よりも重要だと思った。