弱い暗号アルゴリズムの使用

破られた、または危険な暗号アルゴリズムの使用

説明

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にも衝突の可能性はあります。

参考資料