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

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

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

推薦する

「アノニマス」が2万件のISアカウントを公開、ビットコインアカウントは永久にロックされた

【概要】国際的に有名なハッカー集団「アノニマス」は、オンライン動画を通じて対テロ戦争への参加を発表し...

Airbnb や他の旅行会社はなぜブロックチェーン技術に興味を持っているのでしょうか?

ブロックチェーン技術は、私たちが想像できるあらゆる業界のあらゆる側面に影響を与える可能性を秘めていま...

ファイルコインの破壊について

Filecoin ブロックチェーン ブラウザでは、Filecoin ネットワーク ブロックの高さ、2...

中央銀行メディア:ビットコインはビットコインに属し、ブロックチェーンはブロックチェーンに属する

(原題: ビットコインはビットコインに属し、ブロックチェーンはブロックチェーンに属します。ビットコイ...

中央銀行デジタル通貨はお金の進化の次のステップになる可能性があるとBISの報告書は述べている

BlockBeatsによると、国際決済銀行(BIS)は、中央銀行デジタル通貨(CBDC)は通貨の未来...

ビットメイン - Antminer A3

マイニングネットワークによると、1月17日午後14時、Bitmainは公式ウェブサイトで新しいマイニ...

ビットコイン価格が54,000ドルまで下落したにもかかわらず、アナリストは依然として「4月は強気」

3月23日、さまざまなオンチェーンデータから、クジラウォレットが購入を減速させ、リスクを個人投資家...

ディストリビューテッド・ラボがウクライナでビットコインウォレットをリリース

Cointelegraphとの独占インタビューで、Distributed LabsのCEOであるV...

最高のビットコインマイナー: Antminer S5 レビュー (パート 1)

今日はいい日だ。 Bitmain から最新のビットコイン マイニング マシン、AntMiner S5...

馬鞍山警察と南京警察が共同で160万元相当のビットコイン採掘機盗難事件を解決

12月23日、玉山支局は市当局と南京警察の強力な支援を得て、さまざまな捜査手法を駆使し、内部者と外...

ファンド大手バンガード:ビットコインがゼロに戻る可能性は高い

新浪米国株ニュース北京時間9日午前、ファンド大手バンガード・グループのチーフエコノミスト、ジョー・デ...

米国国土安全保障省は、セキュリティとプライバシーを主な懸念事項として、Factomなどのブロックチェーンプロジェクトに投資している。

9/11テロ攻撃の余波を受けて設立された米国国土安全保障省は、ブロックチェーン技術への関心を高めて...

Bifu.com 潘国利:取引は感情に頼らない

今年9月、デジタル通貨業界で初めて専門的なデータ分析と投資アドバイスを提供するウェブサイト「BiFu...