説明
DES/3DES、RC2/RC4、Blowfishなどの古い暗号や、AES-ECBなどの不適切なモードは、用途と設定によって機密性や完全性を損なうおそれがあります。DESの短い鍵、RC4の既知の弱点、ECBの反復ブロックのパターン露出は、それぞれ異なる問題です。MD5とSHA-1は衝突攻撃に弱く、衝突耐性が必要な完全性検査や署名には適しません。
想定される影響
- 弱い暗号化による平文パターンや機密情報の露出
- ハッシュの衝突を利用した完全性検査や署名の偽造
- 弱い暗号設計でトークンやメッセージを偽造できる場合の認証・認可の回避
対処方法
- AES-GCMやChaCha20-Poly1305などの認証付き暗号と、SHA-256/384/512やSHA-3などのハッシュを優先してください。
- これらのセキュリティ用途では、DES/3DES、RC2/RC4、Blowfish、MD5、SHA-1、AES-ECBを避けてください。
- AES-GCMには推奨の96ビットIVを使ってください。同じ鍵でIVを再利用せず、鍵ごとの使用量と衝突リスクを管理してください。鍵は最低128ビット、可能なら256ビットにしてください。
- パスワードからの鍵導出にはPBKDF2、Scrypt、Argon2を使い、適切な反復回数やメモリコストを設定してください。
- AEADを優先し、使えない場合はHMAC-SHA-256などで別途認証してください。
- Web Crypto API(
node:cryptoのwebcrypto)やlibsodiumなど、実績のあるライブラリを使ってください。
例
変更前
javascript
const crypto = require("crypto");
// 変更前: 弱い暗号モードとハッシュです。
function encryptEcb(plaintext, key) {
// AES-ECBはパターンを露出し、完全性を保護しません。
const cipher = crypto.createCipheriv("aes-128-ecb", key, null);
return Buffer.concat([cipher.update(plaintext, "utf8"), cipher.final()]);
}
function md5Checksum(data) {
// MD5はセキュリティ上の完全性検査に必要な衝突耐性がありません。
return crypto.createHash("md5").update(data).digest("hex");
}
// デモ用の固定鍵です(これも危険です)。
const key = Buffer.alloc(16, 0);
console.log(encryptEcb("secret text", key).toString("hex"));
console.log(md5Checksum("file-contents"));
変更後
javascript
const crypto = require("crypto");
// 機密性と完全性のためのAES-256-GCMです。
function encryptAesGcm(plaintext, key) {
const iv = crypto.randomBytes(12); // 推奨の96ビットIVです。
const cipher = crypto.createCipheriv("aes-256-gcm", key, iv);
const ciphertext = Buffer.concat([
cipher.update(plaintext, "utf8"),
cipher.final(),
]);
const tag = cipher.getAuthTag();
return {
iv: iv.toString("hex"),
ct: ciphertext.toString("hex"),
tag: tag.toString("hex"),
};
}
function decryptAesGcm(enc, key) {
const iv = Buffer.from(enc.iv, "hex");
const ct = Buffer.from(enc.ct, "hex");
const tag = Buffer.from(enc.tag, "hex");
const decipher = crypto.createDecipheriv("aes-256-gcm", key, iv);
decipher.setAuthTag(tag);
const plaintext = Buffer.concat([decipher.update(ct), decipher.final()]);
return plaintext.toString("utf8");
}
// 完全性検査に強いハッシュを使います。
function sha256Checksum(data) {
return crypto.createHash("sha256").update(data).digest("hex");
}
// 必要に応じてメッセージ認証にHMAC-SHA-256を使います。
function hmacSha256(data, key) {
return crypto.createHmac("sha256", key).update(data).digest("hex");
}
// 強い鍵を生成します(AES-256は32バイト)。
const key = crypto.randomBytes(32);
const enc = encryptAesGcm("secret text", key);
console.log(enc);
console.log("sha256:", sha256Checksum("file-contents"));
console.log("hmac:", hmacSha256("message", crypto.randomBytes(32)));
説明:
- 変更前: AES-ECBは同じ平文ブロックを露出し、暗号文を認証しません。MD5の衝突は、衝突耐性を必要とする完全性検査を損ないます。デモの固定鍵も暗号化したデータを危険にさらします。
- 変更後: AES-256-GCMで認証付き暗号を使い、復号時にタグを検証します。SHA-256は信頼できる基準ハッシュとの比較に使えますが、鍵なしのハッシュは送信者を認証しません。強い鍵と毎回新しいIVを生成し、鍵ごとの使用量を制限して再利用を防いでください。ランダムなIVにも衝突の可能性はあります。