説明
DES/3DES、RC2/RC4、IDEAなどの古い方式や弱いアルゴリズム、ECBなどの安全でないモード、MD5、SHA-1、SHA1withRSA、MD5withRSA などの弱いハッシュや署名は、機密性と完全性を損なうおそれがあります。ECBはブロックの繰り返しを露出させます。DESは鍵が短く総当たり攻撃に弱く、DES/3DESの64ビットブロックは、大量のデータでの衝突やSweet32攻撃の原因になります。MD5とSHA-1の衝突は署名や完全性の検証を破る可能性があります。また、高速な汎用ハッシュはパスワードの推測を容易にします。
想定される影響
- パターンの露出、弱い鍵、暗号解析によるデータの漏えい
- ハッシュの衝突を利用したファイル、トークン、更新プログラムの署名の偽造
- MD5やSHA-1で保存したパスワードへの、レインボーテーブルやGPUを使った総当たり攻撃によるアカウント侵害
- NISTやPCI DSSなど、適用される暗号要件への不適合
対処方法
- 共通鍵暗号にはAES-128以上、可能であれば
AES/GCM/NoPaddingなどの認証付き暗号を使用してください。署名には2048ビット以上の鍵を使うSHA256withRSA、またはP-256以上のECDSAを使用してください。 - ECBは使用しないでください。GCM/CCMなどの認証付きモード、または適切なHMACを組み合わせたCBCなどで完全性も保護してください。
- 暗号学的なハッシュや署名に使う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は認証付き暗号で、同じ鍵で再利用しないNonceと128ビットのタグを使い、改ざんを検証します。再送攻撃の防止には別途プロトコル側の制御が必要です。
PBKDF2WithHmacSHA256はソルトと多い反復回数により、パスワード推測のコストを高めます。SHA256withRSAは衝突に強いハッシュを署名に使用します。