スマート コントラクト開発必読: これら 10 の Solidity セキュリティ問題は無視できません

登链社区
本文约3645字,阅读全文需要约15分钟
スマート コントラクトのセキュリティが懸念されています。2020 年の Solidity における 10 の一般的なセキュリティ問題について学びましょう。

編集者注: この記事は以下から引用しましたデンリアンコミュニティ、許可を得てOdailyによって転載されました。

編集者注: この記事は以下から引用しました

デンリアンコミュニティ

デンリアンコミュニティ

、許可を得てOdailyによって転載されました。

2018 年、私たち (CheckMarx) は、Solidity で記述されたスマート コントラクトに焦点を当て、スマート コントラクトのセキュリティの状態に関する予備調査を行いました[1]。当時、私たちは公開されているコントラクトのソース コードに基づいて、スマート コントラクトのセキュリティ問題の上位 10 件をまとめました。 2 年が経過し、調査を更新し、スマート コントラクトのセキュリティがどのように進歩したかを評価する時期が来ました。

その他の注目すべき問題

セキュリティ問題のランキングがあるのは良いことですが、詳細の一部がランキング リストと一致しないため、興味深い詳細が含まれる傾向があります。トップ 10 の問題をさらに深く掘り下げる前に、注目に値する元の研究のハイライトのいくつかを説明する必要があります。

2018 年の上位 2 つの問題は、外部契約によるサービス拒否と再入でした。しかし現在では、これらの問題は軽減されました(それでも懸念は残ります)。リエントランシーについて詳しくは、当社の研究ブログ「スマート コントラクトに関するセキュリティの視点」[2] をご覧ください。

翻訳者注: 実際、DeFi アプリケーション (フラッシュ ローンなど) の組み合わせにより、深刻なリエントリー攻撃が数多く発生しています。

Solidity v0.6.x がリリース [3] され、多くの重大な変更がもたらされています [4] が、スキャンされたスマート コントラクトの 50% は、Solidity v0.5.0 コンパイラに対応する準備さえできていません。さらに 30% のスマート コントラクトは古い構文 (例: sha3、throw、constant などの使用) を使用しており、83% のコントラクトには指定されたコンパイラ バージョンに仕様上の問題 (プラグマ) があります。

翻訳者注: Solidity 0.6 は意味的により明確であり (たとえば、0.6 バージョンは継承 [5] の点でアップグレードされています)、コンパイラーが問題を適時に検出し、コードをより安全にするのに役立ちます。

可視性の問題[6]は、2018 年も今年もトップ 10 に含まれていませんでしたが、可視性の問題の 48% 増加は注目に値します。

以下の表は、2018 年と 2020 年のトップ 10 FAQ リストの変化を比較しています。これらの問題は、重大度と蔓延度の順に並べられています。

1. チェックされていない外部呼び出し

   if(!addr.send(1)) {
     revert()
   }

未チェックの外部呼び出しは、2018 Solidity トップ 10 セキュリティ問題リストで 3 番目に多い問題です。最初の 2 つは現在解決されており、チェックされていない外部呼び出しが 2020 年の更新リストで最も一般的な問題となっています。

Solidity の基礎となる呼び出しメソッド (address.call() など) は例外をスローしません。代わりに、エラーが発生すると false が返されます。

また、コントラクトを使用して ExternalContract.doSomething() を呼び出す場合、doSomething() が例外をスローすると、例外は「バブル」して伝播し続けます。

失敗した場合は、戻り値をチェックして明示的に処理する必要があります。addr.send() を使用した次のイーサ転送が良い例です。これは他の外部呼び出しにも当てはまります。

   for(uint256 i=0; i< elements.length; i++) {
       // do something
   }

2. 高コストサイクル

High Cost Loops は、Solidity Security リストの 4 位から 2 位に上がりました。この問題の影響を受けるスマートコントラクトの数は約 30% 増加しました。

イーサリアムでの計算には支払いが必要であることは誰もが知っています。したがって、操作を完了するために必要な計算を削減することは、最適化の問題 (効率) だけでなく、コストの問題でもあります。

ループ処理は負荷の高い操作です。その良い例を次に示します。配列に含まれる要素が増えるほど、ループを完了するためにより多くの反復が必要になります。最終的に、無限ループは利用可能なガスをすべて使い果たしてしまいます。

攻撃者が要素配列の長さに影響を与えることができる場合、上記のコードはサービス妨害を引き起こします (実行はループから抜け出すことができません)。スキャンされたスマート コントラクトでは、コントラクトの 8% に配列の長さの操作の問題があることが判明しました。

3. 圧倒的な所有者

   function calculateBonus(uint amount) returns (uint) {
       return amount/DELIMITER*BONUS;
   }

これは、Soldiity のトップ 10 のセキュリティ問題の中で新たに発生している問題であり、契約の約 16% に影響しており、一部の契約はその所有者 (Owner) と密接に関連しており、次の例に示すように、一部の関数は所有者のアドレスによってのみ呼び出すことができます。

コントラクト所有者のみが doSomething() 関数と doSomethingElse() 関数を呼び出すことができます。前者はonlyOwner デコレーターを使用し、後者はそれを明示的に実装します。これは重大なリスクをもたらします。所有者の秘密キーが危険にさらされると、攻撃者がコントラクトを制御する可能性があります。

4. 算術精度の問題

   function transferTo(address dest, uint amount) {    
         require(tx.origin == owner) {      
                  dest.transfer(amount);  
         }
   }

Solidity のデータ型は、256 ビット仮想マシン (EVM[7]) を使用しているため、やや複雑です。 Solidity は浮動小数点演算を提供せず、32 バイト未満のデータ型は同じ 32 バイト スロットにパックされます。これを念頭に置くと、次のようなプログラムの精度の問題が発生することが予想されます。

上の例に示されているように、乗算の前に実行される除算には、大きな丸め誤差が生じる可能性があります。

5. tx.origin に依存する

   for (uint i = border; i >= 0; i--) {  
         ans += i;
   }

悪意のあるコントラクトが中間者攻撃を実行し、すべての資金を流出させる可能性があるため、スマート コントラクトは認証に tx.origin に依存すべきではありません。代わりに msg.sender を使用することをお勧めします。<、>Tx Origin 攻撃の詳細な説明は、Solidity のドキュメント [8] に記載されています。簡単に言えば、tx.origin は常にコントラクト呼び出しチェーンの最初の開始者アカウントであり、msg.sender は直接呼び出し元を表します。チェーン内の最後のコントラクトが認証に tx.origin に依存している場合、チェーンの途中でコントラクトを呼び出すと、呼び出されたコントラクトから資金を排出できます。これは、認証では誰 (msg.sender) が実際に作成したのかが確認されないためです。呼び出し。

6. オーバーフロー(オーバーフロー/アンダーフロー)

Solidity の 256 ビット仮想マシンにはオーバーフローとアンダーフローの問題があります (訳者注: 結果が値の範囲を超えるため、オーバーフローと呼ばれます)。ここ [9] には具体的な分析が記載されています。 for ループ条件で uint データ型を使用すると、無限ループが発生する可能性があるため、開発者は特に注意する必要があります。

上の例では、i の値が 0 の場合、次の値は 2^256 -1 となり、条件は常に true になります。開発者は使ってみるべきです

、!= および == は比較用です。

   for (var i = 0; i < elements.length; i++) {
      // to something
   }

7. 安全でない型の推論

この問題は、Solidity のセキュリティ問題トップ 10 リストで 2 つ順位を上げ、現在では以前より 17% 以上多くのスマート コントラクトに影響を与えています。

Solidity は型推論をサポートしていますが、いくつかの奇妙な動作があります。たとえば、リテラル 0 は、通常予期される整数ではなく、バイト型であると推測されます。

以下の例では、i の値を uint8 として格納できれば十分であるため、i の型は uint8 であると推論されます。ただし、要素配列に 256 を超える要素が含まれている場合、次のコードはオーバーフローします。

予期しない動作やエラーを避けるために、データ型を明示的に宣言することをお勧めします。

   if(!addr.send(1)) {    
      revert()
   }

翻訳者注: var 定義変数は Solidity 0.6 で削除されました (Solidity 0.6 以降は型推定はありません)。新しいコンパイラを使用する場合は問題ありません。

8. 誤転送

この問題は、Solidity のセキュリティ問題トップ 10 リストの 6 位から 8 位に下がり、現在影響を受けているスマート コントラクトの割合は 1% 未満です。

   for (uint i = 0; i < users.lenghth; i++) {
      users[i].transfer(amount);
   }

契約間でイーサを転送するにはいくつかの方法があります。公式には addr.transfer(x) 関数を使用することが推奨されていますが、依然として send() 関数を使用しているスマート コントラクトが見つかりました。

転送が失敗した場合、addr.transfer(x) は自動的に例外を発生させ、最初の未チェックの外部呼び出しの問題を軽減することに注意してください。

9. インサイクル転送

   if (timeHasCome == block.timestamp) {    
       winner.transfer(amount);
    }

ループ本体でイーサ転送を行う場合、転送の 1 つが失敗すると (たとえば、コントラクトが受信できないなど)、トランザクション全体がロールバックされます。<、>この例では、攻撃者はこの動作を悪用してサービス拒否攻撃を実行し、他のユーザーがイーサを受信できないようにする可能性があります。

2018 年には、タイムスタンプの依存関係の問題が 5 位にランクされており、スマート コントラクトは異なる時間に複数のノードで実行されることを覚えておくことが重要です。 Ethereum Virtual Machine (EVM) はクロック時間を提供せず、タイムスタンプを取得するために通常使用される now 変数 ( block.timestamp のエイリアス) は、実際にはマイナーが操作できる環境変数です。

要約する

マイナーは現在の環境変数を操作できるため、不等式 > でのみ使用できます。

= と <= はその値を使用します。

要約する

ソースリンク:securityboulevard.com