ブロックチェーンを語る(24):スマートコントラクトのセキュリティ脆弱性

ブロックチェーンを語る(24):スマートコントラクトのセキュリティ脆弱性

スマート コントラクトは、今後 10 年間で最も重要なプログラミング言語になりつつあります。その登場により、約 30 年にわたる集中型プログラミング方式が覆され、オープンで透明性があり、改ざんのない信頼できる運用環境が社会全体にもたらされましたが、同時に、前例のない技術的課題と、防止が困難なセキュリティ上の脆弱性にも直面しています。これらの課題や脆弱性の一部は設計自体から生じますが、その他の課題や脆弱性は新しい分散オペレーティング環境から生じます。この記事では、Ethereum スマート コントラクトの歴史の中で出現した、または知られているセキュリティの脆弱性を整理し、スマート コントラクトに取り組んでいる、またはこれから取り組む人への警告として役立ちます。

注: この記事では、スマート コントラクトのプログラミング言語として Solidity を使用します。

次の 2 つのコードを見てみましょう。

 アドレス addr = 0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b;
 if(!addr.call.value(20 ether)()){
     投げる;
 }

同様に:

 アドレス addr = 0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b;
 if(!addr.send(20 ether)){
     投げる;
 }

これらのコードは両方とも、20 イーサを契約アドレス 0x6c8f に送信します。 2 番目のコードには脆弱性はありませんが、最初のコードには重大なセキュリティ上の脆弱性があります。なぜ?

まず、addr.call.value()() (注: 括弧が 2 つあり、最初の括弧は転送する Ether の量の割り当て、2 番目の括弧はメソッド呼び出しです) と addr.send() の違いを見てみましょう。どちらも特定のアドレスに Ether を送信し、新しいメッセージの呼び出しです。違いは、これら 2 つの呼び出しのガス制限が異なることです。 send() は 0 ガス (call.gas(0).value()() と同等) を返しますが、call.value()() は全 (現在残っている) ガスを返します。

注: フォールバック関数をガスなしで呼び出す必要がある場合、EVM はガスを 2300 以下に自動的に調整します。

セキュリティ脆弱性1: フォールバック機能

スマート コントラクトを呼び出すときに、指定された関数が見つからない場合、または呼び出す関数を指定しなかった場合 (Ether の送信など)、フォールバック関数が呼び出されます。

フォールバック機能は、過剰な処理を行わないように設計されています。合理的なアプローチは、フォールバック関数でいくつかのログ (またはイベント) を出力して、クライアント (通常は web3.js) に関連情報を通知することです。したがって、この呼び出しにガスを送信しない場合 (send() と同様に)、システムはフォールバック関数を実行するためにデフォルトで上限 2300 ガスを使用します。

しかし、addr.call.value()() 経由で Ether を送信する場合、状況は異なります。 send() と同様に、フォールバック関数が呼び出されますが、フォールバック関数に渡される使用可能なガスは、残りのガスすべてです (大量になる場合があります)。このとき、フォールバック関数はさまざまなこと (ストレージへの書き込み、新しいスマート コントラクトの再度の呼び出しなど) を実行できます。悪意のある目的で適切に設計されたフォールバックは、システムに損害を与えるさまざまなことを実行する可能性があります。

したがって、このセキュリティ脆弱性を回避するための結論は、 call.value() ではなく、常に send() を使用して ether を送信するというものです。

セキュリティ脆弱性2: 再帰

次のコードを見てください。

 関数withdrawBalance() {
     引き出し金額 = userBalances[msg.sender];
     (引き出し金額>0)の場合{
         if (!(msg.sender.call.value(amountToWithdraw)())) { throw; }
         ユーザー残高[msg.sender] = 0;
     }
 }

これは、ユーザーがお金を引き出すためのコードであり、ユーザーはスマート コントラクトから預金を一度に引き出すことができます。たとえば、契約アカウントに合計 1,000 イーサがあり、ユーザーが 10 イーサを持っているとします。コードには重大な再帰呼び出しの脆弱性があるため、ユーザーはアカウント内の 1,000 イーサをすべて簡単に引き出すことができます。

まず、コードは send() の代わりに addr.call.value()() を使用して ether を送信し、フォールバック関数の呼び出しに十分なガスを提供します。フォールバック関数を次のように記述することで、すべてのイーサを取り除くことができます。

 関数 () {
     アドレス addr = 0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b;
     COUNT<100の場合{
         addr.call("withdrawBalance");
         カウント++;
     }
 }

このフォールバック コードでは、カウンターが 100 未満の場合に、withdrawBalance 関数が再帰的に呼び出されます。この場合、

 msg.sender.call.value(引き出し金額)()

100 回呼び出され、100*10 イーサが奪われます。

したがって、スマート コントラクトを作成するときは、それが再帰的に呼び出される可能性があることを考慮する必要があります。この場合、再帰呼び出しによって発生する問題を防ぐために、次のようにコードを調整できます。

関数withdrawBalance() {
     引き出し金額 = userBalances[msg.sender];
     ユーザー残高[msg.sender] = 0;
     (引き出し金額>0)の場合{
         if (!(msg.sender.call.value(amountToWithdraw)())) {
              userBalances[msg.sender] = 引き出し金額;
             投げる;
          }
     }
 }

セキュリティ脆弱性 3: 呼び出し深度制限

呼び出しの深さは 1024 に制限されています。EVM では、スマート コントラクトはメッセージ呼び出しを通じて他のスマート コントラクトを呼び出すことができます。呼び出されたスマート コントラクトは、メッセージ呼び出しを通じて他のコントラクトを呼び出し続けることも、それらを呼び戻すこともできます (再帰的)。ネストされた呼び出しの深さは 1024 に制限されます。

次のコードを考えてみましょう。

 関数sendether(){
     アドレス addr = 0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b;
     addr.send(20 ether);
     //送信はtrueを返すべきだと思う
     var thesendok = true;
     //送信に関して何かを行うとOKが返される
     ...
 }

そして、もう一方のフォールバック関数は次のように定義されます。

 関数(){
     //何もしない
 }

相手側がフォールバック方法を明確に定義しているため、自分のコードは間違いなく安全であると考えています。しかし、あなたは間違っています。攻撃者は、1023 個のネストされた呼び出しを作成し、sendether() を呼び出すだけで、add.send(20 ether) が失敗し、他の呼び出しが成功するようになります。コードは次のとおりです。

 関数ハック(){
     var カウント = 0;
     while(count < 1023){
         this.hack();//このキーワードはメッセージ呼び出しになります
         カウント++;
     }
     if(count==1023){
         thecallingaddr.call("sendether");
     }
 }

したがって、深さ制限の問題を解決するには、次のように、呼び出しの深さが増加するたびに呼び出しの戻りが正しいかどうかを確認するのが正しい書き方になります。

関数sendether(){
     アドレス addr = 0x6c8f2a135f6ed072de4503bd7c4999a1a17f824b;
     if(!addr.send(20 ether)){
         投げる; //誰かが私をハッキングした
     }
     //送信はtrueを返すべきだと思う
     var thesendok = true;
     //送信に関して何かを行うとOKが返される
     ...
 }

スマート コントラクトのこれら 3 つの基本的な脆弱性について理解できたので、次は、有名なThe DAO事件でハッカーがどのようにしてこれらの脆弱性を利用して Ether を盗み出し、Ethereum 開発史上最も壊滅的な事件を引き起こしたのかを見てみましょう。

DAOの脆弱性

DAO の脆弱性は、上記の最初の脆弱性と 2 番目の脆弱性を組み合わせたものです。次のコードを見てください。

 関数splitDAO(
     uint _提案ID、
     アドレス_newCurator)noEther onlyTokenholdersは(bool _success){を返します
    ...
    uint 移動予定資金 =
         (残高[msg.sender] * p.splitData[0].splitBalance) /
         p.splitData[0].totalSupply;
    if(p.splitData[0].newDAO.createTokenProxy.value(fundsToBeMoved)(msg.sender)
        == false) をスローします。
    ...
     報酬を撤回します(msg.sender);
    totalSupply -= balances[msg.sender];
    残高[msg.sender] = 0;
    paidOut[msg.sender] = 0;
    true を返します。
 }

ハッカーは、次のコードを複数回呼び出すことで、Ether の複数のコピーを転送しました。

 p.splitData[0].newDAO.createTokenProxy.value(資金移動先)(msg.sender)

彼はどうやってそれをやったのですか?とても簡単です!契約が実行されると:

報酬を撤回します(msg.sender);

そうなると、対応する関数に入ります。

関数 withdrawRewardFor(アドレス _account)
    noEther内部は(bool _success)を返します{
    ...
    if(!rewardAccount.payOut(_account,reward)) //脆弱性コードthrow;
    ...
 }

payOut 関数は次のように定義されます。

関数payOut(address _recipient, uint _amount)は(bool)を返します{
    ...
    if(_recipient.call.value(_amount)) //脆弱性コード PayOut(_recipient, _amount);
        true を返します。
    }それ以外{
        false を返します。
    }
 }

これを見ると、これは先ほど示した例とほぼ同じであることがおわかりいただけると思います。このコードは、send() ではなく addr.call.value()() 経由で Ether を送信するため、ハッカーに攻撃の余地が残ります。ハッカーは、フォールバック関数を作成し、その関数内で再度 splitDAO() を呼び出すだけで済みます。

おそらくあなたは、この記事を数か月前に読んでいたら、The DAO のハッカーになって数百万ドル相当の Ethereum を送金できたかもしれないと考えているかもしれません。実際、The DAO の脆弱性はコード レベルで明らかだったため、The DAO が実装される前からオンラインで発見され警告されていましたが、実際に攻撃が発生するまで真剣に受け止められていませんでした。

DAO事件はイーサリアムコミュニティ全体に大きな影響を与え、スマートコントラクトのセキュリティが最も重要かつ緊急の問題となりました。スマート コントラクト プログラミングにおけるさまざまなセキュリティ上の脆弱性をすべての人に認識させることによってのみ、将来的にスマート コントラクトの安全性がさらに高まります。

<<:  エンタープライズイーサリアムアライアンスは新しいガバナンスモデルを採用する可能性がある

>>:  欧州議会:規制当局はブロックチェーン取引に法的地位を与えるべき(完全なレポートをダウンロード)

推薦する

100元未満から数千元まで、成都のビットコイン投機家は3年間で5倍の利益を上げました

「昨日は私を無視したけど、今はもう私に構ってられないわよ。」この文は現時点ではビットコインに非常に当...

時価総額上位 10 位のビットコイン エコシステム トークンのうち、いくつ所有していますか?

ビットコインのエコシステムが発展し続けるにつれて、主要な暗号通貨に対する市場の姿勢が明確になってきて...

米金融安定監視評議会:ビットコインは金融安定に対する「脅威」となる可能性がある

金融安定監督評議会(FSOC)は、ビットコインとブロックチェーン技術が金融の安定に対する脅威であると...

Coinbase と Binance に対する SEC の訴訟は暗号通貨業界にどのような変化をもたらすでしょうか?

かつて、活気に満ちた暗号通貨の世界には、常に支配的な2つの巨人、 BinanceとCoinbaseが...

公式延期が恒例となりましたが、Filecoin先物に参加できますか?

この記事は元々、IPFS Force ZoneのCarlによって書かれました。 「Filecoinの...

JuBi.com、Shark Coin、そして黄氏の間で一体何が起こったのでしょうか?

イベントレビュー9月22日、楊城晩報は、東莞市南城に住む黄さんがオンラインで仮想通貨を取引していたと...

コインゾーントレンド: 今週のビッグデータに基づくビットコインの価格動向 (2016-05-3)

1. 価格動向価格は休日中ずっと 2900 前後で推移しました。昨日の午後、通貨価格はサポートレベ...

Dell 700W 電源装置のレビュー

電源モデル: DELL NPS-700AB Aブランド: Dell定格電力: 700W電源外観外観コ...

人民元安はビットコイン価格を押し上げることはできない

怒りの解説:人民元とビットコインの価格の連動性が弱まっているようだ。中国のマクロ経済への懸念もビット...

なぜビットコインは米国市場で安全資産と見なされているのでしょうか?

米国市場では、仮想通貨取引を提供するコインベースが4月14日に上場する。コインベースは2021年第1...

数百万台のマイニングマシンが中国に返送される:輸送費は1000万元を超え、破損率は20%

四川省やその他の地域で洪水の季節が近づく中、電気料金の値下げを求めて、一部の鉱山労働者は鉱山を建設し...

CZ: 仮想通貨の冬は18か月続く可能性があるが、2025年には強気相場が到来すると楽観視

暗号通貨取引所バイナンスのCEO、チャンポン・ジャオ(CZ)氏は今週、Twitter Spaceで定...

中央銀行デジタル通貨に関する研究と議論:仮想通貨とその規制対応

近年、インターネットの世界で生まれた仮想通貨は各方面から注目を集めており、特にビットコインに代表され...

元米国財務長官:暗号通貨は「一種のデジタルゴールド」であり、今後も存在し続ける

元米国財務長官のローレンス・サマーズ氏はブルームバーグテレビの「ウォールストリート・ウィーク」に出演...