暗号化は「アルゴリズムを選べば終わり」ではなく、鍵をどう用意して安全に運用するかで成否が決まります。対称鍵と公開鍵の使い分け、キー/IV(nonce)の生成、漏えいを防ぐ保管とローテーション、AzureでのMicrosoft管理鍵とCMKの選択まで、実装者目線で整理します。
暗号化実装で最初に押さえるべき前提
暗号化の事故は、暗号そのものよりも鍵の扱いで起きます。最初に次の前提をそろえると、設計がブレにくくなります。
- 暗号化は機密性(読めない)を守る手段。改ざん検知(完全性)まで必要なら、方式選びが変わる。
- 鍵を失うとデータは永久に復旧できないことがある(特にアプリ暗号化・クライアント側暗号化)。「漏えい対策」と同じくらい「喪失対策」も重要。
- 暗号化は設計・実装・運用の三位一体。運用(権限、監査、ローテーション、障害対応)を先に決めるほど安全になる。
- 暗号アルゴリズムは自作しない。信頼できる暗号ライブラリとOSの安全な乱数を使う。
方式選定:対称鍵暗号と公開鍵暗号の役割分担
結論から言うと、「データ本体は対称鍵」が基本です。公開鍵暗号は「鍵配布」「署名」「対称鍵を包む(エンベロープ暗号)」で真価を発揮します。
| 観点 | 対称鍵暗号(共通鍵) | 公開鍵暗号(非対称鍵) |
|---|---|---|
| 主用途 | データ暗号化(大量データ向き) | 鍵交換・署名・対称鍵(データ鍵)のラップ |
| 速度 | 高速(実運用の本命) | 比較的遅い(頻繁な大容量暗号化には不向き) |
| 鍵の扱い | 同じ秘密鍵で暗号化・復号するため、配布・保管が難しい | 公開鍵は配布可/秘密鍵は厳重管理(守る対象が明確) |
| 実装の落とし穴 | IV(nonce)再利用、KDF不備、鍵の埋め込み | 秘密鍵の漏えい、誤った用途(署名鍵で暗号化等)、鍵長・形式の混在 |
| 現実的な組み合わせ | 公開鍵暗号で対称鍵(データ鍵)を安全に配布し、データは対称鍵で暗号化する(エンベロープ暗号) | |
実務の定番:エンベロープ暗号で「ローテーションしやすい」構造にする
鍵運用を現実的にするための王道がエンベロープ暗号です。ポイントは、暗号化対象のデータを直接「長期鍵」で守らないことです。
- DEK(Data Encryption Key / データ鍵):データを暗号化する短命の対称鍵(レコード単位・ファイル単位・セッション単位などで生成)
- KEK(Key Encryption Key / 鍵暗号鍵):DEKを包むための長期鍵(KMS/HSM/Key Vaultで管理)
暗号文と一緒に保存するのは、通常次のセットです。
- 暗号化されたデータ本体(ciphertext)
- IV/nonce(後述。多くの場合、秘密ではない)
- 認証タグ(AEADの場合)
- KEKで暗号化(ラップ)したDEK(encrypted DEK)
- 鍵バージョン情報(どのKEKで包んだか)
この構造にしておくと、KEKをローテーションしても「データ本体の再暗号化」ではなく、DEKの再ラップで済むケースが増え、運用負荷が一気に下がります。
対称鍵暗号:キーの生成は「乱数の質」がすべて
対称鍵のキーは、原則として暗号学的に安全な乱数(CSPRNG)で生成します。自作乱数や一般的な擬似乱数(例:Math.random、一般用途のRandom)から作るのは避けてください。
安全なキー生成の基本ルール
- キー長はアルゴリズムの推奨値を採用(例:AESなら128/256ビット、ChaCha20なら256ビット)。
- キーはバイナリのまま扱う(Base64等は表現形式であって暗号ではない)。
- 生成は暗号ライブラリやOS APIに任せる(言語標準の暗号モジュールを優先)。
- キー素材をログに出さない(デバッグログ、例外、監査ログに載せない)。
「パスワードからキーを作る」ならKDFが必須
ユーザーのパスワードや短い文字列から暗号鍵を作らざるを得ない場合、単純なハッシュ(SHA-256を1回など)では不十分です。KDF(鍵導出関数)を使い、総当たり攻撃に強い形へ変換します。
| KDF | 特徴 | 向いている用途 | 実装時の要点 |
|---|---|---|---|
| Argon2id | メモリ耐性が高い(GPU/ASICに強い設計) | パスワードベース暗号化の第一候補 | メモリ量・反復回数・並列度を「環境に合わせて」設定し、将来調整できるよう保存する |
| scrypt | メモリコストを調整できる | Argon2が使いにくい環境の代替 | パラメータ(N/r/p)を保存し、性能と防御のバランスを取る |
| PBKDF2 | 互換性が高く広く実装される | レガシー互換が必要な場合 | 反復回数を十分に高くし、ソルトは必須(ユーザーごと・データごとに一意) |
さらに実務では、ソルトに加えてサーバー側だけが持つpepper(秘密の追加要素)を併用すると、防御層が増えます。ただしpepperは「別の鍵」と同様に扱い、KMS/Key Vault等で保護してください。
IV(nonce)の作り方・扱い方:秘密ではないが、再利用は致命的
対称鍵暗号では、キーと並んでIV(初期化ベクトル)/nonceの扱いが事故の温床です。多くの方式でIV/nonceは秘密にする必要がありませんが、同じキーでの再利用が破滅的になり得ます。
IV/nonce運用の鉄則
- 暗号化のたびに必ず変える(ユニークにする)。同一キーで使い回さない。
- IV/nonceは暗号文と一緒に保存してよい(ただし改ざんは検知できる形にする)。
- 可能なら、ライブラリがnonce生成まで面倒を見るAPIを使う。
- どうしても自前で生成するなら、ランダムか単調増加カウンタを採用し、衝突しない設計にする。
| 方式(例) | IV/nonceの考え方 | 再利用した場合の典型リスク | 実務のコツ |
|---|---|---|---|
| AES-GCM(AEAD) | nonceは「同一キーで一意」が必須 | 平文の関係が漏れる、改ざん検知が破られる可能性 | 12バイトnonceが一般的。ランダム生成なら衝突確率の設計に注意(大量暗号化ではカウンタ方式も検討) |
| ChaCha20-Poly1305(AEAD) | nonce一意が必須 | 情報漏えい、認証破りの危険 | APIがnonceを要求する場合は生成・保存・一意性を設計に組み込む |
| AES-CBC(非AEAD) | IVはランダム推奨 | パターン漏えい、パディングオラクル等の複合リスク | 単独で使わず、必ずHMAC等で完全性を担保(ただし設計が難しいのでAEAD優先) |
特にAEADでは「nonceの一意性」が安全性の前提になっていることが多く、“秘密じゃないから雑に扱ってよい”ではない点に注意してください。
改ざん検知まで含めるならAEADを第一候補にする
暗号化の目的が「盗み見防止」だけでなく、改ざん防止(完全性)も含むなら、AEAD(Authenticated Encryption with Associated Data)を優先すると設計が単純になります。
- 暗号化の結果として「暗号文」に加えて認証タグが得られる
- 復号時にタグ検証が失敗したら、平文を扱わず即エラーにできる
- ヘッダー情報(ユーザーID、テナントID、バージョンなど)をAAD(追加認証データ)として保護できる
実装の重要ポイントは、「復号より先に検証」です。ライブラリによっては復号APIが検証込みになっているため、その形を選ぶとミスが減ります。
公開鍵暗号:鍵ペアの生成と、秘密鍵の扱いが核心
公開鍵暗号は、対称鍵とは別の落とし穴があります。最大のポイントは秘密鍵を外へ出さないこと、そして用途に合った方式を選ぶことです。
公開鍵暗号の用途を分けて考える
| やりたいこと | 適した技術 | 要点 |
|---|---|---|
| 通信を安全にする | TLS(証明書) | 基本は自前実装せず、TLSに寄せる。鍵は証明書ストアやKMSで保護。 |
| 署名(改ざん検知・本人性) | ECDSA / EdDSA(例:Ed25519) | 署名鍵は暗号化鍵と分離し、用途混在を避ける。 |
| 鍵交換(共有鍵を作る) | ECDH 等 | 生成される共有鍵はKDFで整形し、最終的に対称鍵として使う。 |
| 対称鍵を包んで配布する | RSA-OAEP / KMSのWrap/Unwrap | “公開鍵で包む/秘密鍵でほどく”を徹底。秘密鍵の保護が最重要。 |
鍵ペアは「信頼できるライブラリ」で生成する
- 鍵生成は暗号ライブラリの標準機能を使う(自作の乱数・自作の鍵生成は避ける)。
- 秘密鍵はエクスポート不可(外部に取り出せない)にできるなら、その形が理想。
- 秘密鍵をファイルに置く場合は、保護(OS保護領域、暗号化、アクセス制御)と運用(更新、失効)を必ずセットで。
鍵管理の本質:鍵は「文字列」ではなく「権限」として扱う
鍵管理を強くするコツは、鍵を単なる秘密文字列としてではなく、“その鍵を使える能力(権限)”として捉えることです。
よくある保管場所の比較
| 保管方法 | 安全性 | 運用性 | 典型的な使いどころ | 注意点 |
|---|---|---|---|---|
| ソースコードに埋め込み | 低 | 一見楽 | 避けるべき | 漏えい範囲が広く、ローテーションが地獄。リポジトリ履歴からも消しにくい。 |
| 設定ファイル(平文) | 低 | 普通 | 避けるべき | 配布先が増えるほど漏えい面が増える。バックアップやログ転送で漏れやすい。 |
| 環境変数 | 中 | 高 | 短期的な暫定策 | プロセスダンプ、誤ログ、運用ミスで漏れる。長期保管先としては弱い。 |
| シークレット管理(KMS/Key Vault等) | 高 | 高 | 基本の推奨 | 権限設計と監査が鍵。アプリはアイデンティティでアクセスし、鍵を最小限に露出。 |
| HSM(ハードウェア) | 非常に高 | 中〜高 | 高要件(規制・金融・重要鍵) | コストと設計が増えるが、秘密鍵の取り出し不可など強力な制御が可能。 |
「鍵をどこに置くか」で迷ったら、基本はKMS/Key Vaultへ寄せ、さらに要件が高い鍵はHSMで扱う、が現実的です。
アクセス制御:最小権限を“具体的な権限”に落とす
「最小権限」と言っても抽象的になりがちです。鍵管理では、次のように分解すると設計しやすくなります。
- 読み取り権限:鍵素材(秘密値)を取得できる権限
- 使用権限:鍵素材は見えないが、暗号/復号や署名/検証などの操作だけできる権限
- 管理権限:作成、無効化、削除、ローテーション、ポリシー変更など
可能なら「読み取り」を避け、“使用のみ許可”に寄せると漏えいリスクが下がります。特に秘密鍵は「使える人」と「管理できる人」を分離できると強いです。
ローテーション設計:鍵を回す前に「復号できる期間」を決める
鍵ローテーションは“回すこと”自体が目的ではなく、漏えい時の被害を限定し、運用の健全性を保つための仕組みです。設計のコツは、ローテーションを「スケジュール」と「緊急対応」に分けることです。
ローテーションに必要な情報をデータ側に持たせる
アプリ暗号化(アプリが自前で暗号化してDB等に保存)では、復号のために次の情報を暗号文とセットで保持するのが定石です。
- 鍵ID(または鍵バージョン)
- IV/nonce
- 認証タグ(AEAD)
- 暗号アルゴリズムの識別子(将来の移行に備える)
これがないと、鍵を更新した瞬間に過去データが復号できなくなったり、移行が詰んだりします。
エンベロープ暗号だとローテーションが現実的になる
KEKを更新しても、暗号化済みDEKを新KEKで包み直す「再ラップ」が中心になり、データ本体の再暗号化を減らせます。大規模システムほど、この差が効きます。
| 設計パターン | KEKローテーション時に必要な作業 | 運用負荷 |
|---|---|---|
| データ本体を長期鍵(KEK相当)で直接暗号化 | 全データの再暗号化(復号→再暗号化) | 高(バッチ処理・停止・リスクが増える) |
| エンベロープ暗号(DEK + KEK) | DEKの再ラップが中心(必要なら段階移行) | 中〜低(設計が正しければオンラインで回しやすい) |
ログ・監査・バックアップ:鍵運用は「漏えい」だけでなく「喪失」に備える
鍵の事故には2種類あります。
- 漏えい:第三者に鍵が渡り、データが読める/偽装できる
- 喪失:正当な運用者ですら鍵を失い、データが二度と復号できない
漏えい対策は意識されやすい一方、喪失対策が抜けると「障害=データ消失」になりがちです。次を最低限セットにしてください。
- 監査ログ:誰が・いつ・どの鍵を・どんな操作で使ったか(特に管理操作)
- バックアップと復旧手順:鍵ストアのバックアップ、復元テスト、緊急時の連絡・承認フロー
- 削除保護:誤操作や侵害で鍵が削除されない仕組み(論理削除、保護設定など)
- ブレークグラス:通常権限ではできない緊急復旧用の手段(多要素・監査・期限付き)
Azureで迷うポイント:Microsoft管理鍵と顧客管理鍵(CMK)の使い分け
Azureを含む多くのクラウドでは、サービス側で既定の暗号化が提供されます。ここでの判断軸は「暗号化するか」ではなく、鍵の管理主体と統制レベルをどこまで求めるかです。
ざっくり結論:要件がなければMicrosoft管理鍵、要件があるならCMK
多くのケースで、既定のMicrosoft管理鍵(サービス管理鍵)で十分な安全性が得られます。一方で、次のような要件があるならCMKが選択肢になります。
- 規制・監査で「鍵を顧客が管理すること」が明確に求められる
- 鍵のローテーションや失効を、自社ポリシーでコントロールしたい
- 特定インシデント時に「鍵を無効化して即座にアクセス不能にする」統制が必要
- 鍵の保管場所(リージョン、HSM等)や運用分離(職務分掌)が重要
| 比較項目 | Microsoft管理鍵 | 顧客管理鍵(CMK) |
|---|---|---|
| 運用負荷 | 低(サービスに任せられる) | 中〜高(鍵のライフサイクルを自社で持つ) |
| 統制 | 標準的 | 高(無効化・ローテーション・監査を自社主導に寄せられる) |
| 失効の効果 | 限定的(サービス仕様に依存) | 強い(鍵を止めると復号できず利用不可にできる設計が多い) |
| 適した場面 | 一般的な業務システム、要件が明確でない場合 | 規制対応、重要データ、監査で鍵統制が求められる場合 |
AzureでCMKを使うときの基本設計(考え方)
サービスごとに設定手順は異なりますが、設計の骨格は共通です。
- 鍵の保管:Key VaultまたはManaged HSMでKEK(ラップ用鍵)を管理する
- アプリの認証:可能ならManaged Identity等で、アプリに秘密情報を持たせずアクセスさせる
- 権限分離:鍵管理者(作成/無効化)と、鍵利用者(wrap/unwrap等)を分ける
- 監査:鍵操作ログを監査基盤へ送る(異常検知やインシデント調査のため)
- 削除保護:誤削除や攻撃で鍵が消えないように保護機能を有効化し、運用手順も整備する
- ローテーション:鍵バージョンを前提に、段階移行できるアプリ設計にする
CMKは「安全になる魔法のスイッチ」ではなく、自社で責任を引き受ける範囲を増やす選択です。必要な統制がある場合は強力ですが、要件が薄いのに採用すると運用負荷だけが増えがちです。
実装イメージ:アプリで暗号化する場合の“安全に壊れない”手順
ここでは、アプリケーション側でPIIや機密属性を暗号化してDBに保存するケースを想定し、壊れにくい手順を示します(概念設計)。
推奨フロー(AEAD + エンベロープ暗号)
- 暗号化のたびに、CSPRNGでDEK(データ鍵)を生成する(例:256ビット)。
- 暗号化のたびに、nonceを生成する(方式が要求する長さで一意に)。
- 平文をAEADで暗号化し、暗号文とタグを得る(必要ならAADにレコード識別子やテナントIDを入れる)。
- DEKをKEKでラップする(KMS/Key Vaultのwrap機能、または公開鍵で包む)。
- DBには「暗号文・nonce・タグ・暗号化DEK・鍵バージョン」を保存する。
- 復号時は、保存された鍵バージョンでDEKをアンラップし、タグ検証込みで復号する。
この形のメリットは、ローテーションや移行が「鍵バージョンの追加」と「再ラップ/再暗号化の段階移行」で進められる点です。
データ形式を決める(将来の移行を楽にする)
暗号化結果を保存するとき、形式がバラバラだと運用が破綻します。おすすめは、次のような“必須フィールド”を固定することです。
- alg:アルゴリズム識別子(例:A256GCMなど)
- kid:鍵ID(Key ID)
- ver:鍵バージョン
- nonce:IV/nonce
- tag:認証タグ
- edek:暗号化DEK(wrapped key)
- ct:暗号文
フィールド名は自由ですが、将来のアルゴリズム移行や鍵更新を前提に、メタ情報を必ず残してください。
よくある落とし穴と、回避策
| 落とし穴 | なぜ危険か | 回避策 |
|---|---|---|
| 鍵をソースコードやリポジトリに埋め込む | 漏えい面が広く、ローテーションも困難。履歴から消えにくい。 | KMS/Key Vaultへ移し、アプリはIDで取得/使用。シークレットスキャンも導入。 |
| IV/nonceを使い回す | 同一キーでの再利用は情報漏えいや認証破りに直結し得る。 | 暗号化ごとに生成し、暗号文とセットで保存。大量処理では一意性設計を明確化。 |
| 暗号化だけで改ざん検知をしていない | 暗号文を改ざんされても気づけず、復号後のデータが壊れる/攻撃が成立する。 | AEADを優先。どうしても分離するなら暗号化+HMACを正しく設計。 |
| 本番と検証で同一鍵を使う | 検証環境の脆弱性が本番の漏えいに直結する。 | 環境ごとに鍵を完全分離。権限・監査・ネットワークも分離。 |
| 鍵の削除・失効手順がない | インシデント時に止められず、復旧時に復号できないなど混乱する。 | 通常ローテーションと緊急失効の手順を分け、訓練(手順書+演習)を行う。 |
| 鍵の喪失を想定していない | バックアップなしで鍵が消えるとデータが永久に失われる。 | 削除保護、バックアップ、復元テスト、ブレークグラスを整備。 |
用途別の現実解:どれを選ぶべきかの早見表
| 用途 | まず検討する解 | 鍵の持ち方 | 補足 |
|---|---|---|---|
| サービス間通信の保護 | TLS | 証明書/秘密鍵はOS保護領域やKMS | アプリ独自暗号化より、まずTLSと適切な証明書運用を固める |
| DBの特定列(PII等)の暗号化 | AEAD + エンベロープ暗号 | KEKをKey Vault、DEKは都度生成してラップ | 鍵バージョンとメタ情報を保存し、移行できる設計に |
| ファイル/オブジェクト暗号化 | ストリーミング対応のAEAD | ファイル単位DEK + KEKラップ | 大容量ではnonce設計と分割暗号化の方式が重要 |
| 署名(改ざん検知・真正性) | 署名アルゴリズム(鍵は暗号化用と分離) | 秘密鍵はHSM/KMS優先 | 署名鍵で暗号化しない。用途混在は事故の元 |
| Azureのストレージ等での暗号化(サービス機能) | 既定(Microsoft管理鍵) | サービス管理 | 要件がある場合のみCMK。採用時は運用責任が増える |
実装者向けチェックリスト(設計レビューに使える)
- 暗号化の目的が「機密性」だけか、「完全性(改ざん検知)」も必要かが明確になっている
- 対称鍵/公開鍵の役割分担が整理され、データ本体は対称鍵で暗号化する設計になっている
- キー生成はCSPRNGで行い、パスワードから鍵を作る場合はKDF(ソルト必須)を採用している
- nonce/IVは暗号化ごとに一意で、保存形式と衝突しない運用が決まっている
- AEADを優先し、タグ検証に失敗したデータは平文として扱わない
- 鍵はアプリに埋め込まず、Key Vault/KMS/HSM等で管理し、最小権限が設計されている
- 鍵バージョンを前提に、過去データの復号と段階移行ができる
- 監査ログ、バックアップ、削除保護、緊急失効(インシデント手順)が揃っている
- 本番・検証・開発で鍵と権限が完全に分離されている
暗号化は「やった感」が出やすい一方で、実際の安全性は鍵運用の設計で決まります。方式(対称鍵/公開鍵)と実装(KDF、nonce、AEAD)を押さえたうえで、Key VaultやKMSを活用し、ローテーションと監査まで含めた“運用できる暗号化”に落とし込むことが、最短で堅牢性を上げる道です。

コメント