고정 초기화 벡터(IV) 사용

고정되거나 재사용되는 초기화 벡터(IV)

설명

CBC는 예측 불가능한 IV가 필요하며, GCM은 같은 키에서 IV가 재사용되지 않아야 합니다. 동일 키로 동일 IV를 재사용하거나 0으로 채운 IV, 하드코딩된 문자열에서 파생된 IV를 쓰면 같은 평문이 같은 암호문 패턴으로 나타나거나(특히 CBC 첫 블록), GCM에서는 nonce 재사용으로 기밀성과 무결성이 동시에 붕괴됩니다. 공격자는 반복되는 패턴을 분석해 평문 관계를 추정하거나, GCM에서는 두 암호문의 XOR로 평문 관계를 드러내고 인증 태그를 위조하여 메시지를 변조할 수 있습니다.

잠재적 영향

  • 암호문 패턴 노출: CBC에서 같은 키와 IV로 암호화한 메시지는 동일한 첫 평문 블록이 동일한 암호문 블록으로 나타나 내용 유추가 쉬워집니다.
  • 평문 관계 유출(GCM nonce 재사용): 같은 키·IV로 암호화된 두 암호문의 XOR로 평문들의 관계가 드러나 기밀성이 약화됩니다.
  • 무결성 붕괴 및 위조: AES-GCM에서 IV(Nonce) 재사용 시 인증이 깨져 공격자가 유효한 태그를 만들어 메시지를 위조할 수 있습니다.
  • 메시지 상관관계 노출: 반복되는 암호문 패턴으로 요청·응답의 연관성을 추정할 수 있습니다. 재전송 방지는 별도의 프로토콜 제어가 필요합니다.

해결 방법

  • 매 암호화 시점마다 새로운 IV 생성: SecureRandom.nextBytes(iv)로 예측 불가능한 IV를 생성합니다. GCM은 일반적으로 12바이트(96비트) 권장.
  • 하드코딩/정적 IV 금지: new byte[N]으로 할당한 배열을 0으로 채워진 상태 그대로 쓰거나, "constant".getBytes() 같은 상수 기반 IV를 사용하지 마세요.
  • IV 재사용 방지 전략: 생성한 IV를 암호문과 함께 저장·전달하고, 같은 키로 재사용되지 않도록 관리하세요. 무작위 IV는 충돌 가능성이 있으므로 키별 사용량과 교체 정책도 관리해야 합니다.
  • 알고리즘 모드 점검: 가능하다면 nonce 오용 저항 모드(AES-GCM-SIV 등) 검토. 단, 기본 원칙은 여전히 고유 nonce 보장입니다.
  • 키/IV 길이 준수: 알고리즘이 요구하는 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에서 같은 키로 0으로 채운 고정 IV를 재사용하면 nonce 재사용이 발생합니다. 이는 기밀성 손상(평문 관계 노출)과 무결성 붕괴(태그 위조 가능)로 이어지며, CBC에서도 고정 IV는 반복 패턴 노출을 유발합니다.
  • 변경 후: SecureRandom으로 매번 12바이트 IV를 생성하고 암호문과 함께 저장·전달합니다. 난수 생성만으로 절대적인 유일성이 보장되지는 않으므로, 키별 사용량을 제한하고 IV 재사용을 방지해야 합니다.

참조