Azure SQL Databaseの機密データ保護!Always Encrypted(常に暗号化)の設定と運用ガイド
生徒
「Azure SQL Database(アジュール・エスキューエル・データベース)に顧客のクレジットカード番号を保存したいのですが、管理者でも中身が見えないようにする方法はありますか?」
先生
「それなら、Azureの最強クラスのセキュリティ機能『Always Encrypted(オールウェイズ・エンクリプテッド:常に暗号化)』が最適ですよ。」
生徒
「普通の暗号化とは何が違うんですか?」
先生
「データベースのエンジン内でもデータが暗号化されたままなので、マイクロソフトの運用者やDB管理者でさえも生データを見ることはできません。具体的な仕組みと設定方法を学んでいきましょう!」
1. Always Encrypted(常に暗号化)とは何か?
Always Encrypted(常に暗号化:つねにあんごうか)は、Azure SQL Database や SQL Server で利用できる高度なデータ保護機能です。従来の暗号化技術と決定的に違う点は、「データの復号(ふくごう:暗号を元に戻すこと)」がデータベース側ではなく、アプリケーション側で行われるという点です。
通常、データベースの暗号化はディスクに書き込む時に暗号化し、読み出す時にデータベースエンジンが復号します。しかし、これではデータベース管理権限を持つユーザー(DBA)がクエリを投げれば中身が見えてしまいます。Always Encrypted を使うと、データはクライアント(C#アプリなど)の中で暗号化されてから送信され、データベースには暗号化された塊(バイナリ)として保存されます。
この技術は「機密性の高いデータ(マイナンバー、クレジットカード番号、健康診断結果など)」をクラウドに預ける際の不安を解消するために開発されました。
2. 二種類のキー:CMKとCEKの役割
Always Encrypted を理解する上で欠かせないのが、2種類の暗号化キー(鍵:かぎ)の存在です。これを理解すると、なぜ安全なのかがよく分かります。
- 列暗号化キー (CEK: Column Encryption Key):実際にテーブルのデータを暗号化するための鍵です。これはデータベース内に保存されますが、次に説明する「CMK」で暗号化された状態で保管されます。
- 列マスターキー (CMK: Column Master Key):CEKを暗号化するための、より上位の鍵です。この鍵の実体は、Azure Key Vault(アジュール・キー・ヴォルト)やクライアントPCの証明書ストアに保管され、データベースには絶対に保存されません。
つまり、鍵を開けるための「親鍵(CMK)」がデータベースの外にあるため、データベースサーバーがハッキングされたとしても、中身を解読することは物理的に不可能なのです。
3. データベース側の準備とSQLによるテーブル作成
まずは、Always Encrypted を適用するためのテーブルを作成しましょう。今回は例として、顧客のメールアドレスとクレジットカード番号を保護するシナリオを想定します。
暗号化される前のテーブルの状態(イメージ)は以下の通りです。
CustomerId | CustomerName | EmailAddress | CreditCardNumber
-----------+--------------+---------------------+-------------------
1 | 佐藤 健太 | sato@example.com | 1234-5678-9012-3456
2 | 鈴木 美紀 | miki@test.jp | 9876-5432-1098-7654
3 | 高橋 浩二 | koji@provider.net | 5555-4444-3333-2222
4 | 田中 舞 | mai@sample.co.jp | 1111-2222-3333-4444
これを Always Encrypted で作成する際の SQL 文は以下のようになります。事前に「列暗号化キー」などが作成されている必要がありますが、定義のイメージを確認してください。
CREATE TABLE [dbo].[Customers]
(
[CustomerId] INT IDENTITY(1,1) PRIMARY KEY,
[CustomerName] NVARCHAR(100) COLLATE Japanese_CI_AS NOT NULL,
-- EmailAddressを「決定論的暗号化」で保護
[EmailAddress] NVARCHAR(256)
ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [MyCEK],
ENCRYPTION_TYPE = DETERMINISTIC,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL,
-- CreditCardNumberを「ランダム化暗号化」で保護
[CreditCardNumber] NVARCHAR(20)
ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [MyCEK],
ENCRYPTION_TYPE = RANDOMIZED,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL
);
4. 決定論的暗号化とランダム化暗号化の違い
先ほどの SQL に登場した「ENCRYPTION_TYPE(暗号化の種類:あんごうかのしゅるい)」には2つの選択肢があります。
- DETERMINISTIC(決定論的:けっていろんてき):同じ入力値に対して、常に同じ暗号化データを生成します。これにより、暗号化されたままでも「完全一致検索」や「GROUP BY」が可能になります。ただし、データパターンから推測されるリスクがわずかにあります。
- RANDOMIZED(ランダム化):同じ入力値でも、毎回異なる暗号文を生成します。より安全ですが、データベース側での検索や演算は一切できなくなります。クレジットカード番号のように、検索キーとして使わない項目に向いています。
5. C#アプリケーションからの接続方法
Always Encrypted で保護されたデータにアクセスするには、アプリケーション側の接続文字列(接続に必要な情報)に特別な設定を加える必要があります。
.NET の Microsoft.Data.SqlClient ライブラリを使用する場合、接続文字列に Column Encryption Setting=Enabled を追加するだけです。これにより、ドライバーが自動的に Azure Key Vault から鍵を取得し、データを透過的に復号してくれます。
using Microsoft.Data.SqlClient;
using System;
// 接続文字列に「Column Encryption Setting=Enabled」を含めるのがポイント
string connectionString = "Server=tcp:yourserver.database.windows.net;Database=yourdb;User ID=user;Password=pass;Column Encryption Setting=Enabled;Authentication=Active Directory Interactive;";
using (SqlConnection connection = new SqlConnection(connectionString))
{
connection.Open();
string sql = "SELECT CustomerName, EmailAddress FROM Customers WHERE CustomerId = @id";
using (SqlCommand command = new SqlCommand(sql, connection))
{
command.Parameters.AddWithValue("@id", 1);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
// アプリ側では自動的に復号された文字列が取得できる
Console.WriteLine($"名前: {reader["CustomerName"]}, メール: {reader["EmailAddress"]}");
}
}
}
}
6. 管理ツール(SSMS)での見え方を確認する
もし、悪意のある管理者が SQL Server Management Studio (SSMS) で直接テーブルを覗いた場合、データはどう見えるでしょうか。
SELECT CustomerName, EmailAddress, CreditCardNumber FROM Customers;
CustomerName EmailAddress CreditCardNumber
佐藤 健太 0x016A3B...(長いバイナリデータ) 0x019D2E...(長いバイナリデータ)
鈴木 美紀 0x012F4C... 0x018B5A...
高橋 浩二 0x015E8D... 0x013A9F...
田中 舞 0x014C7A... 0x017D1C...
このように、正しいマスターキーを持っていないユーザーには、ただの「意味不明な16進数の羅列」にしか見えません。これが Always Encrypted の力です。
7. PowerShellを使ったキーの自動生成と設定
手動で Azure ポータルから設定することも可能ですが、運用では PowerShell(パワーシェル)を使って自動化することが一般的です。以下のコマンドは、Azure Key Vault をマスターキーの保管先として指定する際の例です。
# Azureにログイン
Connect-AzAccount
# 列マスターキー(CMK)のメタデータをSQL Databaseに登録
$akvKey = Get-AzKeyVaultKey -VaultName "MySecureVault" -Name "MasterKey01"
New-AzSqlDatabaseColumnMasterKey -ResourceGroupName "MyRG" -ServerName "myserver" -DatabaseName "mydb" -Name "CMK1" -KeyStoreProviderName "AZURE_KEY_VAULT" -KeyPath $akvKey.Id
# 実行結果
ResourceGroupName : MyRG
ServerName : myserver
DatabaseName : mydb
ColumnMasterKey : CMK1
この作業により、データベース側は「どこに親鍵があるか」という住所だけを知っている状態になります。
8. 運用上の注意点と制限事項
非常に強力なセキュリティを誇る Always Encrypted ですが、導入にはいくつか注意点(デメリット)もあります。
- パフォーマンスへの影響:クライアント側で暗号化・復号の処理を行うため、CPUのリソースをわずかに消費します。大量のデータを一括で扱う場合は処理時間が延びる可能性があります。
- 検索の制限:ランダム化暗号化を選んだ列については、
WHERE句での検索やLIKE検索(部分一致:ぶぶんいっち)が一切使えません。 - 型変換の制限:暗号化された列は、データベース側での自動的な型変換(暗黙の型変換:あんもくのかたへんかん)ができなくなります。アプリ側で厳密に型を合わせる必要があります。
最近では、これらの制限を緩和する「Secure Enclaves(セキュア・アンクレイブ)」という高度な機能も登場していますが、まずは基本の Always Encrypted をマスターすることが重要です。
9. 既存のデータを暗号化する手順
すでに運用中のデータベースに後から Always Encrypted を適用する場合、単に SQL を実行するだけでは不十分です。なぜなら、既存の「生データ」を「暗号化データ」に変換する必要があるからです。
この作業には、SSMS の「列の暗号化ウィザード」を使用するのが最も簡単です。ウィザードは裏側で以下のような処理を代行してくれます。
- 一時的なテーブルを作成する。
- 既存データをクライアント側に一度ダウンロードし、暗号化する。
- 暗号化したデータを新しいテーブルにアップロードし直す。
- 古いテーブルと入れ替える。
データ量が多い場合は、ダウンタイム(サービス停止時間)が発生するため、アクセスの少ない時間帯に作業を計画しましょう。
10. セキュリティのベストプラクティス
Always Encrypted を導入しても、運用が疎かでは意味がありません。以下のポイントを意識して運用しましょう。
- 鍵のローテーション:定期的に列マスターキー(CMK)を更新する仕組みを検討してください。Azure Key Vault を使えば、古い鍵を保持したまま新しい鍵に切り替える「ローテーション」が比較的容易に行えます。
- 権限の分離:Azure の権限設定(RBAC:アールバック)を使い、「データベースを操作できる人」と「Azure Key Vault の鍵にアクセスできる人」を明確に分けることで、内部不正をより強力に防げます。
- バックアップの保護:データベースのバックアップ自体も暗号化されていますが、復元先のサーバーで鍵が利用できるように設定しておく必要があります。
クラウド時代のデータ保護は「境界線で守る」だけでなく「データそのものを守る」という考え方が主流です。Always Encrypted は、その核心を担う機能と言えるでしょう。
まとめ
Azure SQL Databaseの強力なセキュリティ機能であるAlways Encrypted(常に暗号化)について、その仕組みから具体的な実装方法、運用上の注意点までを詳しく解説してきました。この技術の最大のメリットは、データの復号に必要な「列マスターキー(CMK)」をデータベースエンジンの外(Azure Key Vaultなど)で管理することにより、クラウド事業者やデータベース管理者(DBA)であっても機密データの内容を閲覧できない状態にできる点にあります。
導入にあたっては、決定論的暗号化(DETERMINISTIC)とランダム化暗号化(RANDOMIZED)の使い分けが重要です。検索が必要な項目には決定論的を、より高い安全性が求められるクレジットカード番号などにはランダム化を選択するという戦略的な設計が求められます。また、アプリケーション側での接続文字列の設定変更や、型変換の制約といった開発面での考慮事項も忘れてはなりません。
Always Encrypted運用のポイント再確認
実際の運用フェーズでは、データの機密性を維持しつつ、システムの利便性を損なわないバランスが大切です。以下の表に、主な特徴と考慮すべき制約をまとめました。
機能項目 | 内容・メリット | 注意点・制約
---------------------+--------------------------------------------+--------------------------------------
データの所在 | 常に暗号化された状態でDBに保存される | DB側での演算(足し算など)が不可
キー管理 | CMKをAzure Key Vault等で分離管理する | キーへのアクセス権限管理が必須
アプリケーション対応 | クライアントドライバーが自動で復号を行う | 接続文字列に特定の設定が必要
検索性 | 決定論的暗号化なら完全一致検索が可能 | 部分一致(LIKE)検索は利用不可
実践的なSQLサンプル:機密データ管理テーブルの運用
例えば、マイナンバーや給与情報などを扱うテーブルを構築する場合、以下のようなSQL構成になります。ここでは、すでに「MyCEK」という列暗号化キーが作成されている前提での記述例です。
-- 機密性の高い社員情報を管理するテーブルの作成例
CREATE TABLE [dbo].[EmployeeSensitiveData]
(
[EmployeeId] INT PRIMARY KEY,
[FullName] NVARCHAR(100) NOT NULL,
-- マイナンバーは「決定論的」で暗号化(検索に利用するため)
[MyNumber] NVARCHAR(12)
ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [MyCEK],
ENCRYPTION_TYPE = DETERMINISTIC,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL,
-- 給与額は「ランダム化」で暗号化(検索には使わないため)
[Salary] INT
ENCRYPTED WITH (COLUMN_ENCRYPTION_KEY = [MyCEK],
ENCRYPTION_TYPE = RANDOMIZED,
ALGORITHM = 'AEAD_AES_256_CBC_HMAC_SHA_256') NOT NULL
);
このテーブルに対して、暗号化が有効なクライアントからデータを投入した後の、データベース内部での見え方は以下のようになります。
EmployeeId | FullName | MyNumber (暗号化済) | Salary (暗号化済)
-----------+----------+-----------------------+-----------------------
101 | 田中 一郎 | 0x01A2B3C4D5E6F... | 0x01F9E8D7C6B5A...
102 | 佐藤 幸子 | 0x01B3C4D5E6F7A... | 0x01E8D7C6B5A49...
103 | 伊藤 健二 | 0x01C4D5E6F7A8B... | 0x01D7C6B5A4938...
104 | 渡辺 直美 | 0x01D5E6F7A8B9C... | 0x01C6B5A493827...
このように、SQL Server Management Studio(SSMS)などのツールで直接テーブルをセレクトしても、中身はバイナリ形式のデータ(16進数)として表示され、内容を推測することはできません。これにより、万が一データベースのバックアップファイルが盗難に遭ったり、特権ユーザーが不正にアクセスしたりしても、実データが流出するリスクを最小限に抑えることができるのです。
Azure SQL Databaseを利用する上で、コンプライアンス要件が厳しいプロジェクトや、個人情報の保護を最優先とするシステムにおいて、Always Encryptedは標準的に検討されるべき機能です。Azure Key Vaultとの連携を深めることで、鍵のライフサイクル管理も自動化でき、より強固な多層防御を実現できるでしょう。
生徒
「先生、Always Encryptedの仕組みがよく分かりました!データベース側には鍵(CMK)を置かないというのが、セキュリティの要なんですね。」
先生
「その通りです。家の中に金庫(データベース)があっても、その鍵(CMK)を銀行の貸金庫(Azure Key Vault)に預けているようなイメージですね。泥棒が家に侵入して金庫を見つけても、開ける術がありません。」
生徒
「なるほど。でも、アプリの開発者がC#でコードを書くときは、あまり難しいことを意識しなくていいんでしょうか?」
先生
「基本的には、接続文字列に Column Encryption Setting=Enabled を追加するだけです。あとはSqlClientドライバーが裏側でやってくれます。ただし、SQLクエリを書くときに少し注意が必要ですよ。例えば、パラメーター化クエリを正しく使う必要があります。」
生徒
「パラメーター化クエリですか。例えばどんな感じですか?」
先生
「暗号化された列に対して値を渡すときは、直接文字列を埋め込むのではなく、必ずSqlParameterを使って型を明示して渡す必要があります。そうしないと、ドライバーがどの値を暗号化すべきか判断できないからです。」
// 良い例:パラメーターを使用して型を一致させる
var cmd = new SqlCommand("SELECT * FROM Customers WHERE EmailAddress = @email", conn);
var param = new SqlParameter("@email", System.Data.SqlDbType.NVarChar, 256);
param.Value = "test@example.com";
cmd.Parameters.Add(param);
生徒
「あ、本当だ。型をしっかり合わせないといけないんですね。実行結果もアプリ側では普通に見えるんですよね?」
先生
「はい。正しい権限を持つアプリから実行すれば、このように見えますよ。」
名前: 佐藤 健太, メール: sato@example.com
生徒
「すごい!DBの中ではぐちゃぐちゃのバイナリなのに、アプリにはちゃんと届いている。これなら安心して顧客情報を預けられますね。」
先生
「その意気です。ただし、パフォーマンスがほんの少し低下することや、LIKE検索ができないといった制限も覚えておいてください。適材適所で使うのがプロの仕事です。次は、さらに高度な『Secure Enclaves』についても調べてみると面白いですよ!」
生徒
「はい!ありがとうございます。まずはこの基本をしっかりマスターして、安全なアプリを作れるよう頑張ります!」