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

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

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

推薦する

トランプ大統領就任初日:国家ビットコイン準備金の設立を優先?

暗号通貨業界は、トランプ次期大統領にビットコイン準備金を積み上げるよう促し、この取り組みに真の政治的...

ビットコインの幸不幸:淘汰産業リストが削除され、来年5月に3回目の半減期が訪れ、資本はギャンブルを続ける

Forex Eyeニュース:12月17日の早朝、ビットコインは7,000ドルを下回りました。慧連の創...

ニューヨーク規制当局、リップルのRLUSDステーブルコインを承認へ

リップルラボのRLUSDステーブルコインは、米ドルに連動した過剰担保のデジタル通貨で、ニューヨーク州...

Google、米国の暗号通貨信託の広告を許可するポリシーを更新

テクノロジー大手のグーグルは、暗号通貨関連の広告ポリシーを更新し、1月末から暗号通貨信託の広告を許可...

BitInstant 創設者 Charlie Shrem は刑務所システムをブロックチェーン上に導入するのか?

クレイジーな解説:チャーリー・シュレムは、ブロックチェーンのスタートアップ企業BitInstantを...

ビットコインの価格は上昇を続ける

7月13日現在、ビットコインは1か月間で1,432元から1,967元に上昇し、この急騰により多くの若...

エルサルバドル人の70%がビットコイン法案の廃止を希望

IudopUCAが最近実施した世論調査によると、エルサルバドル人の70%がナジブ・ブケレ大統領が支持...

分散型台帳は分散型発電システムの応用を促進することになるだろう

クレイジーコメント:米国ブルックリンでは、ブロックチェーンを使用したピアツーピアの電力取引が成功しま...

ビットコインがブロックされない理由

著者 |顧孟廷、転載の際は出典を明記してください多くの国がビットコインなどの暗号通貨に対して厳しい規...

ビットコインは無駄に上昇したが、ブラックロックのCEOは高い感情的知性で反応した。これは暗号通貨への関心に対する「報復的な反発」だ

月曜日の米国株式市場が開いた直後、海外メディアのコインテレグラフは、ブラックロックのスポットビットコ...

スカート指数の観点からビットコイン強気相場について語る

第0章 はじめにこの記事は真剣な議論ではなく、ただの楽しみのためです。第1章 ヘムラインインデックス...

ビットコインETFはテストに合格しようとしており、当局は承認が市場のボラティリティ抑制に役立つと述べた。

中国証券ネットワークニュース(記者:王卓傑)米証券取引委員会は3月11日にウィンクルボス・ビットコイ...

タイム誌の表紙に登場したトランプコンセプトコインの徹底レビュー

北京時間12月12日夜、タイム誌はトランプ次期米大統領を今年の「パーソン・オブ・ザ・イヤー」に選出す...

市場分析:マイナーはBTC価格の上昇に興味がなく、この市場の波はリスクに直面している

アナリストによると、BTC 価格は上昇を続けており、短期的な損失を回復した後も、日次レベルでは依然と...

コインコード自体の新しい視点からビットコインとその競合コインを観察してみましょう

さまざまな電子通貨の長所と短所をどのように評価するかについては、人によって意見が異なります。視点が違...