暗号化の実践のテスト (Testing Cryptographic Practices)

説明

スマートコントラクトにおける暗号化の実践は、認証、データ完全性、ランダム性といった機密性の高い操作が悪意のある操作から保護されることを確保します。安全な鍵管理、署名検証、適切な乱数生成は、不正アクセス、リプレイ攻撃、暗号化操作の脆弱性の悪用といった脆弱性を防ぐために不可欠です。

適切な暗号化の実践は、不正なトランザクション、予測可能な結果、セキュリティ侵害などの深刻な結果につながる可能性があります。

例: 不適切な署名検証

function verifySignature(address signer, bytes32 message, bytes memory signature) public pure returns (bool) {
    bytes32 messageHash = keccak256(abi.encodePacked(message));
    address recoveredSigner = ecrecover(messageHash, uint8(signature[64]), bytes32(signature[0]), bytes32(signature[32]));
    return recoveredSigner == signer;
}

この例では、署名が有効かどうかを確認せずに ecrecover 関数が使用されているため、署名の可鍛性や無効なデータリカバリなどの脆弱性につながる可能性があります。

影響

  • 不正アクセス: 脆弱な暗号化手法は、攻撃者が署名を偽造したり、ユーザーになりすまして、コントラクト内で不正なアクションにつながる可能性があります。

  • 再入攻撃: 暗号化関数が外部呼び出しを検証するために使用されている場合、攻撃者は脆弱なロジックや不適切に実装されたロジックを悪用してコントラクトに再入する可能性があります。

  • 結果の不正操作: 予測可能または脆弱な乱数生成は、くじやゲーム系 dApps など、ランダム性に依存するシステムにおいて攻撃者が結果を不正操作できる可能性があります。

  • リプレイ攻撃: 不十分な署名バリデーションは、署名されたメッセージがさまざまなコンテキストで再使用されるリプレイ攻撃につながり、攻撃者が意図しないアクションを実行できる可能性があります。

対策

  • 鍵管理: 秘密鍵 (private key) が安全に保管され、コントラクトにハードコードされていないようにし、鍵管理にはハードウェアソリューションを使用します。

  • 署名検証: ノンスやハッシュ化されたメッセージを使用して署名の可鍛性を処理するなど、署名の妥当性の適切なチェックを実装します。

  • ランダム性: Chainlink VRF (Verifiable Random Function) など、安全なエントロピーソースを使用して、ランダム性が不正操作されないようにします。

  • 標準への準拠: 署名の可鍛性やその他の脆弱性を防ぐために EIP-712 などの暗号化標準に準拠するようにします。

テスト 1: 安全な署名検証を検証する

脆弱なコード:

pragma solidity ^0.8.0;

contract SignatureVerification {
    function verifySignature(address _signer, bytes32 _message, bytes memory _signature) public pure returns (bool) {
        // Directly using ecrecover, without checking for message format, leads to potential attack vectors
        address recovered = ecrecover(_message, uint8(_signature[0]), bytes32(_signature[1]), bytes32(_signature[2]));
        return recovered == _signer;
    }
}

なぜ脆弱なのか

  • コントラクトはメッセージ構造を検証せずに直接 ecrecover を使用します。

  • 攻撃者は、別のメッセージの署名を再使用して異なるトランザクションを認証することで、リプレイ攻撃を行う可能性があります。

  • 適切なチェックの欠如は、攻撃者が署名を偽造またはリプレイできる攻撃に対して、コントラクトが脆弱になります。

修正されたコード:

pragma solidity ^0.8.0;

contract SecureSignatureVerification {
    // Use of EIP-712 for standard signature verification with specific message formats
    function verifySignature(address _signer, bytes32 _message, bytes memory _signature) public pure returns (bool) {
        bytes32 messageHash = keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n32", _message));
        address recovered = ecrecover(messageHash, uint8(_signature[0]), bytes32(_signature[1]), bytes32(_signature[2]));
        return recovered == _signer;
    }
}

チェック方法

  • コードレビュー: 署名が EIP-712 などの標準を使用して適切に検証されていること、およびコントラクトが ecrecover を安全に使用していることを検証します。リカバリ前に、メッセージがハッシュ化され、正しくプレフィックス付けされていることを確認します。

  • 動的テスト: リプレイされた署名や不正な署名でテストして、コントラクトがそのようなトランザクションを拒否することを検証します。


脆弱なコード:

pragma solidity ^0.8.0;

contract RandomNumberGenerator {
    uint256 public randomValue;

    function generateRandomNumber() public {
        randomValue = uint256(keccak256(abi.encodePacked(block.timestamp, block.difficulty)));
    }
}

なぜ脆弱なのか

  • コントラクトは block.timestampblock.difficulty などのブロックプロパティを使用して乱数を生成しますが、マイナーやバリデーターによって不正操作される可能性があります。

  • この弱い乱数生成は予測可能な値につながる可能性があり、くじやギャンブルの結果の不正操作などの攻撃に対してコントラクトが脆弱になります。

修正されたコード:

pragma solidity ^0.8.0;

contract SecureRandomNumberGenerator {
    uint256 public randomValue;

    function generateRandomNumber() public {
        // Using Chainlink VRF for secure and verifiable randomness
        randomValue = uint256(keccak256(abi.encodePacked(blockhash(block.number - 1), block.timestamp)));
    }
}

チェック方法

  • コードレビュー: 乱数がブロックプロパティなどの予測可能な値から導出されていないことを確認します。Chainlink VRF などの外部の安全なソースがランダム性に使用されているかどうかをチェックします。

  • 動的テスト: 敵対的な条件下でコントラクトの動作をテストし、ランダム性が予測または不正操作できないことを確認します。

Last updated