はじめに
今回は、ConsenSysがGitHubで公開しているベストプラクティスの文章を参考にSolidityでセキュアなコードの書き方の基礎について説明します。
本記事は、過去にコンセンサス・ベイスが主宰していたオンラインサロンの記事です。記事は2017年~2018年にかけて執筆されたため、一部は、既に古くなっている可能性があります。あらかじめご了承ください。
今回の内容
第1章では基本的な考え方、第2章では外部呼び出しについて説明します。
ゴール
Solidityでセキュアなコードを書くための基礎をマスターできます。
ターゲット
Solidityの基本的な書き方を習得している方
前提知識
Solidityの基本的な書き方をマスターしている。
第1章 基本的な考え方
まずはスマートコントラクト開発者の基本的な考え方を説明していきます。
失敗に備える
- 最悪の場合に契約を一時停止する
- 扱う金額を制限する
- バグ修正のためのアップグレード
公開に対して慎重になる
- テストを徹底的に行う
- testnetでリリースしてバグを発見する
- 一気に公開せずに段階的に公開する
コントラクトコードはできるだけシンプルに書く
- コントラクトコードのロジック
- コントラクトや関数のコードを減らすためのコードのモジュール化
- できるだけ既存のツールやコードを使用する
- できるだけパフォーマンスを明確にする
- 分散化を必要とする部分のみブロックチェーンを使用する
最新の状態に保つ
- 新たに見つかったバグに当てはまらないか自分のコントラクトコードを確認する
- ツールやライブラリはできるだけ早くアップデートする
- 新しいセキュリティ技術を採用する
ブロックチェーンの特性に注意する
- 悪意のあるコードを実行して制御フローを変更しようとする外部からのコールには十分に注意する
- public functionは公開されていて誰でも呼べることを留意する。データも見られていることに注意する
- GasコストとGas Limitに注意する
第2章 外部呼び出し
スマートコントラクトの外部呼び出しについて説明していきます。
外部呼び出しはできるだけ避ける
外部のコントラクトコードからのコールは予期せぬリスクやエラーを含んでいる場合があります。悪質なコードを実行される可能性があります。よって外部のコントラクトコードからのコールの受け付けはできるだけ最小限にするべきです。
send(),transfer(),call.value()()のトレードオフに注意
send(),transfer(),call.value()()は全てEtherの送信に使われます。
- x.transfer(y)とif (!x.send(y)) throw;は同等の意味を表しますが、sendはtransferの低いレベルの送信であるので、できるだけtransferを使用する方が良いです。
- someAddress.send()とsomeAddress.transfer()はリエントラント性があります。これらのメソッドはコントラクトコードを実行します。実行されたコントラクトコードにはイベントを記録するには十分な量である2,300Gasが与えられます。
- someAddress.call.value()()は与えられたEtherを送信して、コントラクトコードを実行します。実行されたコントラクトコードには実行可能な全てのGasが与えられます。ただし、リエントラント性はありません。
send()とtransfer()はリエントラント性があるが、fallback関数が2300Gasを必要とするコントラクトコードには使えません。
トレードオフのバランスをとるパターンの一つとしてはpushとpullのメカニズムを両方使うことです。send()とtransfer()を使う場合はpushコンポーネントを使い、call.value()()を使う場合はpullコンポーネントを使う方が良いです。
リエントラント(reentrant)とは
関数の最初の呼び出しが完了する前に、繰り返し呼び出される可能性のある関数が含まれている。 これにより、関数のさまざまな呼び出しが破壊的な方法で相互作用する可能性があります。詳しくは第3回で説明します。
外部呼び出しのエラー処理
Solidityはaddress.call()、address.callcode()、address.delegatecall()、address.send()などのメソッドを提供しています。これらのメソッドは例外をthrowしないが、例外が発生した場合はfalseを返すようになっています。一方でExternalContract.doSomething()などのcontract callsは自動的にthrowしてくれます。
// bad
someAddress.send(55);
someAddress.call.value(55)(); // this is doubly dangerous, as it will forward all remaining gas and doesn't check for result
someAddress.call.value(100)(bytes4(sha3("deposit()"))); // if deposit throws an exception, the raw call() will only return false and transaction will NOT be reverted
// good
if(!someAddress.send(55)) {
// Some failure code
}
ExternalContract(someAddress).deposit.value(100);
外部呼び出し後の制御フローは仮定しない
ExternalContractが信頼できない場合に悪質なコードが実行されることを前提にコールを使用するのかコントラクト呼び出しを使用するのかを決定します。ExternalContractが悪意のあるものではないとしても呼び出す契約によって悪質なコードが実行される可能性があります。特に危険なのが悪質なコードが制御フローを乗っ取って競合状態に陥ることです。
競合状態
外部のコントラクトコードを呼び出す際の主な危険の1つとしては、制御フローを引き継ぎ、呼び出し関数が期待していなかったデータを変更することができることです。 このようなバグはいろいろな形を取ることができ、DAOの崩壊につながったバグもこの種のバグでした。
信用できない契約には印をつける
外部のコントラクトコードとやりとりする時は変数、メソッド、コントラクトインターフェイスの名前を安全かどうかわかるように示す方が良いです。
Bank.withdraw(100); // Unclear whether trusted or untrusted
function makeWithdrawal(uint amount) { // Isn't clear that this function is potentially unsafe
Bank.withdraw(amount);
}
// good
UntrustedBank.withdraw(100); // untrusted external call
TrustedBank.withdraw(100); // external but trusted bank contract maintained by XYZ Corp
function makeUntrustedWithdrawal(uint amount) {
UntrustedBank.withdraw(amount);
}
参考URL:
https://github.com/ConsenSys/smart-contract-best-practices/
Copyright 2016 Smart Contract Best Practices Authors
Licensed under the Apache License, Version 2.0 (the “License”); you may not use this file except in compliance with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an “AS IS” BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
免責事項
本記事に掲載されている記事の内容につきましては、正しい情報を提供することに務めてはおりますが、提供している記事の内容及び参考資料からいかなる損失や損害などの被害が発生したとしても、弊社では責任を負いかねます。実施される際には、法律事務所にご相談ください。
技術・サービス・実装方法等のレビュー、その他解説・分析・意見につきましてはblock-chani.jp運営者の個人的見解です。正確性・正当性を保証するものではありません。本記事掲載の記事内容のご利用は読者様個人の判断により自己責任でお願いいたします。
ブロックチェーン学習に最適の書籍の紹介
図解即戦力 ブロックチェーンのしくみと開発がこれ1冊でしっかりわかる教科書
本書は、ブロックチェーン技術に興味を持ったエンジニアや、その仕組みを学び、自分の仕事に活かしたいビジネスパーソンを対象にして、ブロックチェーンのコア技術とネットワーク維持の仕組みを平易な言葉で解説しています。この本を読んだうえで、実際にコードを書くような専門書、ブロックチェーンビジネスの解説書を読むことで、理解度が飛躍的に高まるでしょう。(はじめにより)
会社紹介
弊社(コンセンサス・ベイス株式会社)は、2015年設立の国内で最も古いブロックチェーン専門企業です。これまでに、大手企業の顧客を中心に、日本トップクラスのブロックチェーンの開発・コンサルティング実績があります。ブロックチェーンに関わるビジネスコンサル・システム開発・教育・講演などご希望でしたら、お気軽にお問い合わせください。
会社ホームページ
https://www.consensus-base.com/