はじめに
今回は、第1回に引き続きSolidityでセキュアなコードの書き方について説明します。
今回の内容
第1章ではassert()とrequire()について、第2章ではその他注意するべきことについて説明します。
ゴール
Solidityでセキュアなコードを書くための基礎をマスターできます。
ターゲット
Solidityの基本的な書き方を習得している方
前提知識
Solidityの基本的な書き方をマスターしている必要がある。
当シリーズの第1回を読んでおくことを推奨します。
第1章 assert()とrequire()について
assert()を使って不変であることを確認する
assertが失敗した場合(不変プロパティが変更された場合など)にassertガードが実行されます。assertガードとはコントラクトの実行を停止することです。例えば、トークン発行コントラクトにおいてトークンとイーサの発行比率は固定されているはずです。これをassert()を使うことで確認することができます。assertガードはコントラクトを一時停止し、アップグレードを許可するなど他の手法と組み合わせることがよくあります。
contract Token {
mapping(address => uint) public balanceOf;
uint public totalSupply;
function deposit() public payable {
balanceOf[msg.sender] += msg.value;
totalSupply += msg.value;
assert(this.balance >= totalSupply);
}
}
assert()とrequire()を正しく使う
Solidity(0.4.10)でassert()とrequire()が導入されました。require(condition)は入力検証に使用します。条件がfalseの場合にはthrowします。assert(condition)は、conditionがfalseの場合でもthrowされます。しかし、内部エラーか、コントラクトが無効な状態になったかを確認するために使用するべきです。
第2章 その他注意するべきこと
小数点の丸めに注意する
すべての小数点の割り算はもっとも近い整数に丸められます。
// bad
uint x = 5 / 2; // Result is 2, all integer divison rounds DOWN to the nearest integer
// good
uint multiplier = 10;
uint x = (5 * multiplier) / 2;
uint numerator = 5;
uint denominator = 2;
Etherを強制的にアカウントに送ることができる
厳密にコントラクトの残高をチェックする不変条件をコーディングすることを心がけましょう。攻撃者は強制的に任意のアカウントにweiを送信することができ、これを防止することはできません。
攻撃者はコントラクトを作成し、selfdestruct(victimAddress)を呼び出すことで攻撃を行うことができます。victimAddressではコードが呼び出されないため防ぐことはできません。
残高ゼロでコントラクトが作成されると想定するべきではない
攻撃者はコントラクト作成前にコントラクトのアドレスにweiを送信することができます。残高ゼロでコントラクトが作成されると想定すべきではありません。
オンチェーン上のデータは公開されている
多くのアプリケーションでは、提供されたデータをある程度非公開にする必要があります。例えば、ゲームやオークションなどです。プライバシーが問題となるアプリケーションを構築する場合は、ユーザーに情報を早期に公開しないように注意してください。
抽象的なコントラクトとインターフェイスのトレードオフに注意する
インターフェイスと抽象コントラクトの両方はカスタマイズ可能で再利用可能です。
インターフェイスはストレージにアクセスできないことや、他のインターフェイスから継承することができないなどの制限があります。
抽象的なコントラクトを継承するコントラクトの場合は、オーバーライドで実装されていない関数をすべて実装すべきです。
fallback関数は簡単に書く
fallback関数は引数がない(または関数が一致しない)メッセージをコントラクトコードから送った場合や、.send()または.transfer() から呼び出され2300ガスしか使えない場合に呼び出されます。.send()または.transfer() でEtherを送信する場合、fallback関数ではイベントを記録することしかできません。2300ガス以上必要な場合は適切な関数を使用してください。
// bad
function() payable { balances[msg.sender] += msg.value; }
// good
function deposit() payable external { balances[msg.sender] += msg.value; }
function() payable { LogDepositReceived(msg.sender); }
関数と状態変数の可視性を明示的に書く
関数と状態変数の可視性を明示的にラベル付けしましょう。関数はexternal、public、internal、 privateを使用することができます。これらの修飾子の違いを理解しましょう。
// bad
uint x; // the default is private for state variables, but it should be made explicit
function buy() { // the default is public
// public code
}
// good
uint private y;
function buy() external {
// only callable externally
}
function utility() public {
// callable externally, as well as internally: changing this code requires thinking about both cases.
}
function internalAction() internal {
// internal code
}
プログラムを特定のコンパイラバージョンに固定する
コントラクトコードは最もテストされたものと同じコンパイラのバージョンでデプロイするべきです。
pragmaを固定することにより未知のバグのリスクが減ります。高い最新のコンパイラなどを使用して、コントラクトコードが間違ってデプロイされないようにできます。コントラクトは他の人によっても開発される可能性があるのでpragmaを見ることによってコンパイラのバージョンを確認することができます。
// bad pragma solidity ^0.4.4;
// good pragma solidity 0.4.4;
バージョンが0.4以前のSolidityにおける0で割った時の注意
バージョン0.4以前のSolidityでは0で割った時に例外をthrowせず、0が返ります。
バージョンが0.4以上で動作していることを確認しましょう。
関数とイベントを区別する
関数とイベントの混同のリスクを防ぐため、大文字とイベントの前に接頭辞を付けましょう。 関数の場合は、コンストラクタを除き、常に小文字で始まります。
// bad
event Transfer() {}
function transfer() {}
// good
event LogTransfer() {}
function transfer() external {}
参考URL:
https://github.com/ConsenSys/smart-contract-best-practices/#favor-pull-over-push-payments
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/