固定された初期化ベクトル(IV)

固定または再利用された初期化ベクトル

説明

CBCには予測できないIVが必要です。GCMでは、同じ鍵でIVを再利用してはいけません。同じ鍵とIVの組み合わせ、ゼロで埋めたIV、固定文字列から作ったIVを使うと、特にCBCの先頭ブロックで暗号文のパターンが漏れるおそれがあります。GCMのNonce再利用は機密性と完全性を損ない、暗号文同士のXORから平文の関係が分かったり、認証タグを偽造されたりするおそれがあります。

想定される影響

  • パターンの露出: CBCで同じ鍵とIVを使うと、同じ先頭平文ブロックから同じ暗号文ブロックが生成されます。
  • 平文間の関係の漏えい: 同じ鍵とIVで生成したGCM暗号文から、平文同士の関係を推測されるおそれがあります。
  • 偽造: GCMのNonce再利用により認証が破られ、有効なタグを偽造されるおそれがあります。
  • メッセージの関連付け: 繰り返されるパターンから、リクエストと応答の関係を推測される可能性があります。再送攻撃の防止には、別途プロトコル側の制御が必要です。

対処方法

  • 暗号化のたびに新しいIVを生成してください。予測できない乱数IVには SecureRandom.nextBytes(iv) を使用します。GCMでは通常12バイト(96ビット)が推奨されます。
  • new byte[N] で確保した配列をゼロのまま使ったり、"constant".getBytes() などの定数由来の値をIVにしたりしないでください。
  • IVを暗号文とともに保存・送信し、同じ鍵で再利用しないように管理してください。乱数IVにも衝突の可能性があるため、鍵ごとの使用量と鍵の交換も管理してください。
  • 必要に応じて、AES-GCM-SIVなどNonceの誤用に耐性があるモードを検討してください。ただし、Nonceを重複させないことが基本です。
  • 必要なIV長を守ってください。AES-CBCでは16バイト、GCMでは通常12バイトです。

例

変更前

java
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class StaticIvBad {
    // BAD: 고정(제로) IV 사용으로 GCM 보안 붕괴
    public byte[] encrypt(byte[] key, byte[] plaintext) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKeySpec sk = new SecretKeySpec(key, "AES");
        byte[] iv = new byte[12]; // 모든 바이트가 0 -> 예측 가능
        GCMParameterSpec spec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.ENCRYPT_MODE, sk, spec);
        return cipher.doFinal(plaintext);
    }
}

変更後

java
import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.ByteBuffer;
import java.security.SecureRandom;

public class RandomIvGood {
    // GOOD: 매번 SecureRandom으로 새 IV 생성하고, IV를 암호문과 함께 저장/전달
    public byte[] encrypt(byte[] key, byte[] plaintext) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        SecretKeySpec sk = new SecretKeySpec(key, "AES");
        byte[] iv = new byte[12]; // GCM 권장 12바이트
        SecureRandom.getInstanceStrong().nextBytes(iv);
        GCMParameterSpec spec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.ENCRYPT_MODE, sk, spec);
        byte[] ciphertext = cipher.doFinal(plaintext);
        // IV || C 형식으로 결합해 저장/전달
        ByteBuffer out = ByteBuffer.allocate(iv.length + ciphertext.length);
        out.put(iv).put(ciphertext);
        return out.array();
    }
}

解説:

  • 変更前: 同じGCM鍵でゼロの固定IVを再利用すると、Nonceが重複し、平文の関係の露出やタグの偽造につながります。CBCでも固定IVはパターンを露出させます。
  • 変更後: 暗号化のたびに SecureRandom で12バイトのIVを生成し、暗号文とともに保存・送信します。乱数生成だけで絶対的な一意性は保証されないため、鍵ごとの使用量を制限し、IVの再利用を防いでください。

参考資料