취약하거나 위험한 암호화 알고리즘 사용

취약한 암호화 알고리즘과 안전하지 않은 암호 모드

설명

오래되었거나 설계상 취약한 알고리즘(DES/3DES, RC2/RC4, IDEA 등)이나 안전하지 않은 모드(ECB), 약한 해시/서명(MD5, SHA-1, SHA1withRSA, MD5withRSA)을 사용하면 기밀성·무결성이 깨질 수 있습니다. ECB는 블록 패턴이 그대로 노출되어 평문 구조가 드러나며, DES는 짧은 키로 인해 무차별 대입에 취약하며, DES/3DES의 64비트 블록은 대량 데이터에서 충돌과 Sweet32 같은 공격의 원인이 됩니다. MD5·SHA-1은 충돌이 실용적으로 가능해 서명 위조나 무결성 우회가 가능합니다. 공격자는 패턴 분석으로 평문을 추론하거나, 약한 해시에 대해 충돌 입력을 만들어 서명을 우회하고, 빠른 해시로 저장된 비밀번호를 레인보우 테이블/브루트포스로 탈취할 수 있습니다.

잠재적 영향

  • 데이터 노출: ECB 패턴 노출, 약한 블록/키로 인한 복호화·키 추측으로 평문이 유출될 수 있음
  • 무결성·서명 위조: MD5/SHA-1 충돌을 이용해 파일·토큰·업데이트 서명을 위조 가능
  • 계정 탈취: 비밀번호를 MD5/SHA-1 등 일반 해시로 저장 시 레인보우 테이블·GPU 브루트포스로 탈취 위험
  • 규정 준수 위반: NIST/PCI DSS 등 최신 기준 미준수로 컴플라이언스 및 감사 실패

해결 방법

  • 강한 알고리즘 사용: 대칭키는 AES-128 이상, 가능하면 AEAD(AES/GCM/NoPadding) 사용. 서명은 SHA256withRSA(2048비트 이상) 또는 ECDSA(P-256 이상)
  • 모드 선택: ECB 금지. 인증된 모드(GCM/CCM) 또는 최소한 CBC+HMAC 등 무결성 보호 병행
  • 해시/서명 교체: MD5, SHA-1, SHA1withRSA, MD5withRSA 금지. SHA-256 이상 사용
  • 비밀번호 저장: 일반 해시 대신 Argon2id, scrypt, bcrypt 또는 PBKDF2 같은 전용 방식을 사용하고 충분한 작업량과 솔트를 적용하세요. PBKDF2-HMAC-SHA256은 최소 600,000회를 기준으로 서버 성능에 맞게 조정하세요.
  • 난수 품질: IV/Nonce/Salt는 SecureRandom(가능하면 getInstanceStrong)으로 생성, IV/Nonce 재사용 금지
  • 키 관리: 최소 키 길이, 키 교체 주기, 안전한 키 보관(KMS/HSM) 적용
  • 정책/구성: 약한 알고리즘 사용을 코드와 설정에서 차단(알고리즘 허용 목록), 라이브러리/프로바이더 최신화

예시

변경 전

java
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Signature;

public class LegacyCrypto {
    // Insecure: ECB exposes plaintext patterns
    public byte[] encryptEcb(byte[] plaintext, SecretKey key) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, key);
        return cipher.doFinal(plaintext);
    }

    // Insecure: MD5 is collision-prone and fast to brute-force
    public byte[] weakDigest(byte[] data) throws Exception {
        MessageDigest md = MessageDigest.getInstance("MD5");
        return md.digest(data);
    }

    // Insecure: SHA1-based signature is deprecated
    public byte[] signSha1(byte[] data, PrivateKey privKey) throws Exception {
        Signature sig = Signature.getInstance("SHA1withRSA");
        sig.initSign(privKey);
        sig.update(data);
        return sig.sign();
    }
}

변경 후

java
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import java.nio.ByteBuffer;
import java.security.PrivateKey;
import java.security.SecureRandom;
import java.security.Signature;
import java.util.Base64;

public class ModernCrypto {
    // Secure: AES-GCM (AEAD) with random 96-bit nonce, 128-bit tag
    public byte[] encryptGcm(byte[] plaintext, SecretKey key) throws Exception {
        byte[] nonce = new byte[12];
        SecureRandom.getInstanceStrong().nextBytes(nonce);
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(128, nonce);
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);
        byte[] ct = cipher.doFinal(plaintext);
        return ByteBuffer.allocate(nonce.length + ct.length).put(nonce).put(ct).array();
    }

    // Secure password hashing with PBKDF2 (salt + high iteration)
    public String hashPassword(char[] password) throws Exception {
        byte[] salt = new byte[16];
        SecureRandom.getInstanceStrong().nextBytes(salt);
        PBEKeySpec spec = new PBEKeySpec(password, salt, 600_000, 256);
        SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
        byte[] hash = skf.generateSecret(spec).getEncoded();
        return Base64.getEncoder().encodeToString(salt) + ":" + Base64.getEncoder().encodeToString(hash);
    }

    // Strong signature with SHA-256
    public byte[] signStrong(byte[] data, PrivateKey privKey) throws Exception {
        Signature sig = Signature.getInstance("SHA256withRSA");
        sig.initSign(privKey);
        sig.update(data);
        return sig.sign();
    }
}

설명:

  • 변경 전:
    • AES/ECB는 동일한 블록이 동일한 암호문을 만들어 패턴이 노출되어 평문 구조를 추정할 수 있습니다.
    • MD5는 충돌 공격이 실용적이며 매우 빠르게 계산되어 비밀번호 추측·레인보우 테이블 공격에 취약합니다.
    • SHA1withRSA는 충돌 가능성으로 서명 위조 위험이 있어 더 이상 권장되지 않습니다.
  • 변경 후:
    • AES/GCM은 무결성 검증을 포함한 AEAD 모드입니다. 같은 키에서 Nonce를 재사용하지 않고 128비트 태그로 변조를 검증합니다. 재전송 방지는 별도의 프로토콜 제어가 필요합니다.
    • PBKDF2WithHmacSHA256은 솔트와 높은 반복 횟수로 해시 계산을 느리게 하여 비밀번호 크래킹 난이도를 크게 높입니다.
    • SHA256withRSA는 충돌 내성이 강한 해시를 사용해 서명 위조 가능성을 낮춥니다.

참조