🔐

Always Encrypted 覚書

に公開

はじめに

株式会社Acompanyの坂本です。

弊社では TEE という秘密計算の技術を使って「顧客データを弊社サーバー上で処理しつつ、弊社社員でも中身を見れない」サービスを開発しています。
TEE 内で処理中のデータは保護できるが、データを TEE 外の DB に保存する際も保護したいという需要があり、調査したところ Always Encrypted という技術を見つけました。

日本語の情報が少なかったので記事にまとめます。

他の暗号化方式との違い

一般的なクラウド DB サービス(Azure SQL Database など)では、TDE(Transparent Data Encryption)によりデータは暗号化されて保存されますが、Always Encrypted の大きな特徴は データがクライアント側で暗号化されてから DB に送られる ことにあります。

鍵を DB サーバーと別のところ(クライアントのローカル環境や Azure Key Vault)に保存しておけば、Azure など DB サーバーの管理者であっても閲覧することはできません。

また、対応ドライバーを使うことで暗号化・復号は透過的に行われるため、アプリケーション側の変更は最小限で済みます。

ただし、DB 側が平文にアクセスできないことで、一部のクエリに制限があります(詳細は後述)。

TEE の概略

今回の記事のメインではありませんが、後述の Always Encrypted with secure enclaves で用いられる enclave の理解にもなるので簡単に解説します。

TEE(Trusted Execution Environment)はハードウェア技術の一つで、CPU 内に 隔離された実行環境 を持っています。
この隔離された環境のことを enclave と呼びます。

enclave 内で処理されるデータは、OS やハイパーバイザーからも保護され、たとえサーバー管理者であっても内部を参照することができないという特徴があります。
つまり、PCの中にPC所有者であっても内部を見ることが出来ないミニPCがあるようなイメージです。

Always Encrypted の概要

Always Encrypted は、Microsoft SQL Server および Azure SQL Database で利用できるデータ暗号化機能です。
クライアント側で暗号化・復号を行うことで、DB 管理者やクラウド事業者であってもデータの中身を見ることができないという特徴があります。

クライアント側暗号化の仕組み

Always Encrypted の特徴は、暗号化・復号がすべてクライアント側で行われることです。

以下の画像は仕組みの概要を示したものです。

図

Always Encrypted では、データは暗号化された状態で SQL Server に保存され、暗号化鍵は SQL Server とは別の場所(Azure Key Vault など)で保管されます。SQL Server には鍵の保管場所の情報のみが登録されています。

クライアントがデータにアクセスする際は、ODBC Driver が SQL Server から暗号化済みデータと鍵の場所を取得し、その情報を元に鍵の保管場所から鍵を取得します。
その後、取得した暗号化済みデータと鍵を用いて クライアント環境上で 復号を行います。
クライアントがデータを SQL Server に保存する際も同様に、Driver がクライアント環境上でデータの暗号化をしてから SQL Server に送信します。

これらの処理は透過的に行われるため、Python などで書いた DB アクセスコードをほとんど変更することなく Always Encrypted の安全性を享受できます。

2つの鍵: CMK と CEK

Always Encrypted では、2種類の鍵を使用します。これは一般的な DEK(Data Encryption Key)/ KEK(Key Encryption Key)アーキテクチャを採用しており、以下のようなメリットがあります。

  • 鍵のローテーションの効率化: DEK だけの場合、鍵を変更するたびに全データを再暗号化する必要があります。KEK を挟むことで、KEK のみ変更すれば再暗号化は DEK だけで済み、データの再暗号化が不要になります。
  • 権限の分離: KEK を DB の外(Key Vault 等)に置くことで、DB 管理者と鍵管理者を分離できます。両方の権限がないとデータを復号できません。

Always Encrypted における DEK/KEK の具体的な名称は以下の通りです。

鍵 正式名称 役割 保管場所
CMK Column Master Key CEK を暗号化する鍵(KEK) Azure Key Vault など(DB の外)
CEK Column Encryption Key 実際にデータを暗号化する鍵(DEK) DB 内(CMK で暗号化された状態)

CEK は暗号化したい列ごとに作成出来ます。
一つの CEK で複数の列を指定することも可能です。

暗号化の種類とクエリの制限

決定的暗号化 vs ランダム暗号化

Always Encrypted には2種類の暗号化方式があります。

方式 特徴 メリット デメリット
決定的 同じ値は同じ暗号文になる = での検索、GROUP BY が可能 パターン分析される可能性あり
ランダム 同じ値でも毎回違う暗号文になる より安全 検索・集計が一切できない

決定的暗号化は、同じ値は同じ暗号文になる形式です。
GROUP BY など、等価比較が必要な場合に使用します。
ただし、同じ値は同じ暗号文になるため、「暗号文Aが100件ある」→「同じ値が100件ある」という情報は漏れます。

ランダム暗号化は、暗号化時にランダム文字列(nonce)を付け足すことで、同じ値でも違う暗号文になる形式です。
安全性は上がりますが、等値比較などのクエリは出来なくなります。

クエリの制限

列が暗号化されているため、SUM や AVG のような集計処理を DB 側で実行することができません。暗号化の種類とクエリ制限の対応の例を以下に示します。

操作 決定的 ランダム
= での比較 ○ ×
IN 句 ○ ×
GROUP BY ○ ×
ORDER BY × ×
LIKE × ×
範囲比較 (<, >) × ×
集計関数 (SUM, AVG) × ×

次に紹介する Always Encrypted with secure enclaves を使うと、一部の制限が緩和されます。

Always Encrypted with secure enclaves

secure enclaves とは、サーバー側に用意された信頼できる隔離された実行環境(前述の TEE)のことです。
クライアントはローカルで CEK を復号した後、この enclave の内部に送信します(送信は TLS などの暗号化通信を用いて行われるため、平文の CEK は enclave 外部に漏れることはありません)。
これにより、サーバーサイドの enclave 内でデータを平文で扱えるようになるため、通常の Always Encrypted では難しかったクエリが対応可能になります。

secure enclaves で可能になること:

  • LIKE による部分一致検索
  • ORDER BY による並び替え
  • 範囲比較 (<, >, BETWEEN)
  • 鍵のローテーションがサーバー側で完結(ネットワーク越しにデータを移動する必要がない)

ただし、利用するためには、Server 側のハードウェアが TEE 対応している必要があります。
また、クライアント側でも secure enclaves 対応の ODBC Driver が必要です。

実装例

ここでは CMK の保存場所として AKV(Azure Key Vault)を用います。

CMK の作成と登録

1. 鍵の作成

Azure Key Vault を使う場合、RSA 2048bit の鍵を作成します。

create_cmk.sh
az keyvault key create \
  --vault-name <your-keyvault-name> \
  --name <cmk-name> \
  --kty RSA \
  --size 2048

2. SQL Server に CMK の場所を登録

鍵自体は Key Vault にありますが、CMK の保存場所を SQL Server に登録する必要があります。

register_cmk_path.sql
CREATE COLUMN MASTER KEY [CMK_Name]
WITH (
    KEY_STORE_PROVIDER_NAME = 'AZURE_KEY_VAULT',
    KEY_PATH = 'https://<vault-name>.vault.azure.net/keys/<key-name>/<version>'
);

CEK の作成

CEK を作成して DB に登録するには、以下のステップが必要になります:

  1. AES 256bit の対称鍵を生成
  2. CMK で暗号化
  3. SQL Server 専用フォーマットに整形
  4. DB に登録

ここで 3のフォーマットが公開されていない ことに注意が必要です。
解析すればフォーマットを割り出すことも可能だと思いますが、今回は安全のため Microsoft が提供する PowerShell コマンドを使用することにします。

create_encrypted_cek.ps1
# Key Vault のアクセストークンを取得
$KvToken = (Get-AzAccessToken -ResourceUrl "https://vault.azure.net").Token

# CMK の設定オブジェクトを作成
$cmkSettings = New-SqlAzureKeyVaultColumnMasterKeySettings `
    -KeyUrl "https://<vault-name>.vault.azure.net/keys/<key-name>/<version>" `
    -KeyVaultAccessToken $KvToken

# CEK の暗号化された値を生成(AES鍵の生成 + CMKで暗号化 + フォーマット整形)
$encryptedValue = New-SqlColumnEncryptionKeyEncryptedValue `
    -TargetColumnMasterKeySettings $cmkSettings `
    -KeyVaultAccessToken $KvToken

# 結果を表示(これをSQL文で使用する)
Write-Output $encryptedValue

生成された値を使って、SQL Server に CEK を登録します:

register_cek.sql
CREATE COLUMN ENCRYPTION KEY [CEK_Name]
WITH VALUES (
    COLUMN_MASTER_KEY = [CMK_Name],
    ALGORITHM = 'RSA_OAEP',
    ENCRYPTED_VALUE = 0x<上で生成した値>
);

ドライバーの設定

クライアント側のドライバーで Always Encrypted を有効にする必要があります。

odbc.py
# pyodbc の例
connection_string = (
    "Driver={ODBC Driver 18 for SQL Server};"
    "Server=<server>;"
    "Database=<database>;"
    "ColumnEncryption=Enabled;"  # これが必要
    # ...
)

これで Always Encrypted の設定は完了です。
設定後は Azure Portal のクエリエディターなどを用いてデータを確認することで、実際に暗号化されていることを確認できます。
Always Encrypted は設定ミスが発生しやすいため、必ず動作確認を行うようにしてください。

まとめ

Always Encrypted は、SQL Server でデータを暗号化して保存するための機能です。
クライアント側で暗号化・復号するため、DB 管理者であっても平文のデータを見ることは出来ないという大きなセキュリティメリットがあります。

参考リンク

Acompany

Discussion