AES暗号化モードの説明 GCMがCBCをほとんどの場合で上回る理由(そしてその例外)
AES単独では完全なアルゴリズム選定とは言えません。暗号化が実際に安全かどうかは、モード——CBC、CTR、GCM、ECB——によって決まります。開発者にとって必要な実用的な解説です。
ドキュメントに「AES-256を使用」と書かれている場合、それは不完全なアドバイスです。AESはブロック暗号であり、1回に128ビットのブロックだけを暗号化します。それに対して モード が、AESが16バイトを超える実際のデータをどう処理し、暗号化されたデータを攻撃者に観察または改ざんされないよう保護するかを決定します。これが間違っていると、ファイル構造が漏れ出る「暗号化された」データや、ビットフィッピング攻撃に脆弱なシステムが生まれます。
完全なアルゴリズム仕様は AES-256-GCM または AES-128-CBC — キー長の後にモードが続きます。この記事は、各モードが実際にどう動くか、GCMがデフォルトとして適している理由、そして何らかの特別な状況で別のものを選ぶべきかを説明します。
まず、ECBが「ミーム」であり、単なるジョークではない理由
ECB(Electronic Codebook)は、最も基本的なモードです。16バイトのブロックごとに、同じキーで独立して暗号化されます。つまり、同じ明文ブロックは同じ暗号化されたブロックを生成します。
典型的な例は、ECBで暗号化されたLinuxペンギン(Tux)です。ビットマップはブロックごとに暗号化されていますが、画像の大きな部分が一色であるため、暗号化された結果でもペンギンの輪郭が明確に残ります。暗号化は数学的に有効です—データがランダム化されていますが、 パターン は保持されます。
実際の用途では、明文に繰り返しがある場合にこれが問題になります:共通のプレフィックスを持つデータベース行、ファイルヘッダー、パッドされたフィールドなど。ECBは構造を漏らします。新しいコードではECBの使用は正当なケースはありません。ECBを使用しているコードをメンテナンスしている場合は、それを置き換える必要があります。
重要なモード
CBC — 最も古いコードで使われるモード
暗号化ブロックチェーンは、各明文ブロックを前の暗号化されたブロックとXORしてから暗号化します。これにより、ECBのパターン問題が解決され、同じ明文ブロックが異なる暗号化されたブロックを生成します。これは、前のブロックの内容に依存するため、各ブロックの暗号化が変化します。
CBCは初期化ベクトル(IV)を必要とします—ランダムな16バイトの値で、最初のブロックのチェーンを開始します。IVは秘密でなくても、ランダムでなければならず、暗号化されたデータと共に送信しなければなりません。
CBCの問題は: 提供する 機密性 は 整合性。中間で暗号化されたデータを改ざんできる攻撃者は、復号結果の特定のビットを予測可能な方法で変更できます。これは、パディングオーバーライド攻撃の基礎であり、実際のシステムを破壊(POODLE、BEAST、Lucky Thirteen)しました。CBCを使用する場合、独立したMAC(メッセージ認証コード)を用いて整合性を確認しないと、脆弱です。このパターンは「暗号化後にMAC」—まず暗号化し、その後暗号化されたデータをHMACで認証し、復号前にMACを検証する—です。この順序を間違えることは、古典的な誤りです。
CBCはまた、並列処理ができない—各ブロックは前の暗号化されたブロックに依存するため—暗号化は並列化できません。復号は部分的に並列化可能です。
CTR — ブロック暗号から流データ暗号化の振る舞い
カウンターモードはAESをストリーム暗号に変換します。明文を直接暗号化するのではなく、連続するカウンターバリューを暗号化し、その結果を明文とXORします。これにより、CTRモードはパディングを必要とせず(任意長のデータに対応)、暗号化と復号の両方で完全に並列化可能であり、暗号化されたデータの任意の位置にランダムアクセスが可能です。
CTRは、完全なIVではなく、ノンス(一度使った番号)を使用します。同じキーでメッセージごとにノンスが一意である必要があります—同じノンスを再利用すると、2つの明文のXORが漏れ出し、これは深刻な問題です。CBCとは異なり、CTRも整合性保護を提供しません。同じように、MACを追加して改ざん耐性を確保する必要があります。
CTRは、ストリーム暗号の特性が必要な場合に意味があります—大きなファイル、ランダムアクセス復号、並列性が重要な高負荷シナリオ—そしてMAC層を別途処理する場合に適しています。
GCM — 認証暗号、1ステップ
ガロア/カウンターモードは、CTRモードに組み込まれた認証タグ(GHASH)を備えています。あなたは機密性と整合性を1つのプリミティブで得られます。認証タグ—通常128ビット—は、復号前に暗号化されたデータが改ざんされていないことを受信者に確認します。別個のHMACステップは不要で、暗号化順序を間違える可能性もありません。
GCMはさらに 追加認証データ(AAD) をサポートします—暗号化されないが、整合性を保護するデータです。ヘッダー、メタデータ、または暗号化されなくても整合性を保護する必要があるデータに適用できます。AADは暗号化されたデータと共に認証タグで検証されます。
GCMのノンスはデフォルトで96ビット(12バイト)です。CTRと同様に、ノンスの再利用は致命的—同じキーとノンスでGCMを使用すると、明文と認証キー(H)が漏れ出し、完全にセキュリティが破壊されます。常に暗号的に安全なRNGでノンスを生成し、シーケンスカウンターから導出しないでください。シーケンスカウンターを使用する場合でも、厳密な一意性保証を持つグローバルカウンターを用いる必要があります。
GCMは並列化可能で、パディングを必要としません。TLS 1.3、Signalプロトコル、そしてほとんどの現代的な暗号化ライブラリで標準的な推奨です。新しいコードを書く場合、GCMがデフォルトです。
モード比較
| モード | 認証付き | 並列化可能 | パディングが必要 | ノンス/IVの再利用リスク | 判定 |
|---|---|---|---|---|---|
| 関連データとともに認証タグを生成し、改ざんがすぐに検出できるようにします | いいえ | はい | はい | N/A(IVなし) | 決して使用しない |
| CBC | なし(MACを追加) | 暗号化:なし / 復号:はい | はい | 中程度 | 古くからの用途のみ |
| CTR | なし(MACを追加) | はい | いいえ | 重要—再利用 = 明文の漏れ | 限定的な用途 |
| GCM | はい(制限付き) | はい | いいえ | 重要—再利用 = 完全な破壊 | デフォルトの選択 |
Node.jsでのAES-256-GCM
ここに、Nodeの組み込み crypto モジュールを使用した完全な暗号化/復号実装を示します—依存関係は不要です:
const crypto = require('crypto');
const ALGORITHM = 'aes-256-gcm';
const KEY_LENGTH = 32; // 256 bits
const NONCE_LENGTH = 12; // 96 bits — GCM default
const TAG_LENGTH = 16; // 128-bit auth tag
function encrypt(plaintext, key) {
const nonce = crypto.randomBytes(NONCE_LENGTH);
const cipher = crypto.createCipheriv(ALGORITHM, key, nonce, {
authTagLength: TAG_LENGTH,
});
const encrypted = Buffer.concat([
cipher.update(plaintext, 'utf8'),
cipher.final(),
]);
const tag = cipher.getAuthTag();
// Prepend nonce + tag to ciphertext for storage/transmission
return Buffer.concat([nonce, tag, encrypted]);
}
function decrypt(ciphertext, key) {
const nonce = ciphertext.subarray(0, NONCE_LENGTH);
const tag = ciphertext.subarray(NONCE_LENGTH, NONCE_LENGTH + TAG_LENGTH);
const data = ciphertext.subarray(NONCE_LENGTH + TAG_LENGTH);
const decipher = crypto.createDecipheriv(ALGORITHM, key, nonce, {
authTagLength: TAG_LENGTH,
});
decipher.setAuthTag(tag);
// Throws if auth tag doesn't match — do not catch this silently
return Buffer.concat([decipher.update(data), decipher.final()]).toString('utf8');
}
// Generate a key (do this once; store it securely)
const key = crypto.randomBytes(KEY_LENGTH);
const message = 'Hello, authenticated encryption.';
const ciphertext = encrypt(message, key);
console.log('Encrypted:', ciphertext.toString('hex'));
const plaintext = decrypt(ciphertext, key);
console.log('Decrypted:', plaintext); // Hello, authenticated encryption.
このコードについていくつかの注意点があります:
- ノンスは各暗号化呼び出しで新しく生成されます —
crypto.randomBytes暗号的に安全です。カウンターを使用する場合、分布一意性の問題を理解していない限り、それを置き換えるべきではありません。 - ノンスと認証タグは暗号化データと共に保存されます — これらは暗号化データと共に移動する必要があります。ノンスは公開され、タグも公開されます。キーだけが秘密です。
decipher.final()認証失敗時に例外を投げます — これは正しい振る舞いです。静かにキャッチして部分的な明文を返さないでください。認証チェックの失敗は、データが改ざんされたか、またはキーが間違っていることを意味します。- キー管理は難しい部分です —
crypto.randomBytes(32)良いキーを提供しますが、保管と回転の方法がアルゴリズム選択よりも重要です。ハードコードされた定数ではなく、セクレットマネージャーを使用してください。
暗号化モードをインタラクティブにテストしたいですか? IO Tools’ AES暗号化/復号化ツール ブラウザでCBCおよびGCMモードで暗号化および復号化を行うことができ、相互運用性の確認や統合のデバッグに役立ちます。暗号的に安全な256ビットキーを生成するには、 AESキー生成器.
IVおよびノンス:実際に重要なルール
ノンス/IVの誤用はアルゴリズム選択よりも現実世界で多くの破壊を引き起こします。ルールは以下の通りです:
- 常にCSPRNGでノンス/IVを生成してください —
crypto.randomBytes()Nodeで、os.urandom()PythonでSecureRandomJavaで。それではなくMath.random()。タイムスタンプではありません。インクリメントカウンターでも、厳密な一意性保証を持つグローバルカウンターで管理されていない限り、使用しないでください。 - GCMノンスの再利用は認証を完全に破壊します — 同じキーとノンスでGCMを使用すると、認証キーHが暴露されます。攻撃者は、任意の暗号化データに対して認証タグを偽造できます。これは理論的ではなく、Forbidden Attackが実際にこの問題を活用して実装に影響を与えた例があります。
- CBC IVの再利用はそれほど深刻ではありませんが、それでも悪です — 選択明文攻撃が実行可能になります。実際には、各メッセージごとに新しいIVを生成してください。
- メッセージ内容からノンスを導出しないでください — 定義されたノンスが予測可能なデータに依存すると、予測可能なパターンが生まれます。ランダム性を使用してください。
CBCまたはCTRが正しい選択になる場合
GCMは常に答えではありません:
- 古くからのシステムとの相互運用性 — あなたがAES-CBCしか話さないシステムと統合している場合、CBCを使用します。MACの要件を明確に記述し、正しく実装(HMAC-SHA256を使用した暗号化後にMAC)してください。
- GHASHハードウェアがない制限環境 — GCMの認証ステップはGHASHを使用し、ハードウェアに専用アクセラレーションがない場合、純粋なブロック暗号操作よりも計算量が重いです。一部の埋め込みターゲットでCTR+CMACが利用可能だがGHASHが利用できない場合、CTRモードが正当化される可能性があります。
- 非常に大きなファイルのストリーミング暗号化で、位置Nの復号が必要な場合 — CTRのランダムアクセス特性が、位置0からN-1までのデータを読み込まなくても位置Nを復号できる場合に有用です。GCMは理論的にはこの機能を提供しますが、認証タグの検証にはすべての暗号化データを処理する必要があり、その点を失います。
- ディスク暗号化 — フルディスク暗号化はXTSモード(ここではカバーされていません)を使用し、GCMはメッセージ暗号化にしか使われません。XTSは固定サイズ、ランダムアクセスセクターに設計されています。
短い要約
プレビューするには AES-256-GCM 新しいコードでは使用してください。各暗号化呼び出しで12バイトのランダムノンスを生成してください。ノンスと認証タグを暗号化データと共に保存してください。認証タグの検証失敗はエラーとして扱い、警告ではなく、GCMがデータが無効であると判断した場合、明文を返さないでください。
既存のCBCコードを検証している場合:暗号化後にMACが正しく実装されているかを確認し、IVがランダムであるか(再利用されていない、シーケンスではないか)を確認し、メンテナンス期間が許される場合にGCMへの移行計画を立ててください。CBCと正しいHMACは破壊されていません—ただ、GCMよりも多くの「足元の罠」があります。
ECB:置き換えるだけです。ECBが「ここでは問題ない」という審査経路は存在しません。
恵 スコアボードが到着しました!
スコアボード ゲームを追跡する楽しい方法です。すべてのデータはブラウザに保存されます。さらに多くの機能がまもなく登場します!
