こちらのページで基本的な使い方を把握した GCP の Data Loss Prevention (DLP) サービスを用いると、文章や画像の中に含まれる、電話番号や氏名および住所といった、個人を特定し得る情報 Personally Identifiable Information (PII) の検出を行えます。更に追加の設定によって、検出した情報を消したり他の文字列で置き換えたり、ハッシュ化したり暗号化することもできます。DLP はその他の機能も備えていますが、ここでは上記の基本的な機能について使い方を記載します。
PII の検出 Inspect を行うためのテンプレートを作成できます。Inspect API を実行する毎に、検出方法を設定することもできますが、テンプレートを作成しておきそれを Inspect API 実行時に指定することで、設定を毎回行う必要がなくなります。
Cloud Console の DLP UI を用いる場合は以下のようにします。
テンプレートを作成します。
「検査 (機密データの検索)」を選択します。
検出したい infoType を選択します。組み込みで用意されているものの一つ PERSON_NAME
を選択してみます。
カスタム infoType を作成することもできます。ここでは簡単な「単語または語句」を選択してみます。
「日本語テスト」または「あいうえお」に合致する文章中の箇所を検知するための infoType として設定しました。
PERSON_NAME
またはカスタムで作成した MY_INFO_TYPE_20210421_3
を検出するテンプレートが出来上がりました。
PII を検出した箇所に対して、何らかの処理を行うためには de-identification API を利用します。
API の呼び出し時には inspectConfig
と deidentifyConfig
を指定します。
inspectConfig
の代わりに inspectTemplateName
を指定することができますが、同様に deidentifyConfig
の代わりに deidentifyTemplateName
を指定することもできます。
De-identification テンプレートの作成を DLP UI から行うためには以下のようにします。
匿名化 (機密データの削除) を選択します。
匿名化のための処理方法を設定します。ここでは「infoType 名での置換」を選択してみます。
「テスト」タブでテンプレートの動作確認を行えます。de-identification API を実行するためには inspectConfig
または inspectTemplateName
を指定する必要があり、DLP UI では、以下のテキストボックスで inspectTemplateName
を指定します。
何も指定しない場合、DLP が既定で用意していると思われる inspectTemplatename
が指定され、組み込みの infoType のみを全て検出するような挙動となります。以下の例のように「日本語テスト」および「あいうえお」が検出されなくなるのはそのためです。
DLP UI における De-identification の「テスト」タブで行った処理を API Method: projects.content.deidentify で行うためには、以下のようにします。
dl-req.json
{
"deidentifyConfig": {},
"inspectConfig": {},
"item": {
"value": "日本語テスト。私の名前は鈴木太郎です。\nあいうえお、かきくけこ。"
},
"inspectTemplateName": "projects/my-project-20210328/locations/global/inspectTemplates/my-inspection-template-20210421-3",
"deidentifyTemplateName": "projects/my-project-20210328/locations/global/deidentifyTemplates/my-deidentification-template-20210421-2"
}
実行例 (my-project-20210328
はお使いのプロジェクト ID に書き換えてください。)
curl -sS -XPOST -H "Authorization: Bearer $(gcloud auth print-access-token)" -H 'Content-Type: applicaiton/json' \
https://dlp.googleapis.com/v2/projects/my-project-20210328/content:deidentify \
-d @dlp-req.json
{
"item": {
"value": "[MY_INFO_TYPE_20210421_3]。私の名前は[PERSON_NAME]です。\n[MY_INFO_TYPE_20210421_3]、かきくけこ。"
},
"overview": {
"transformedBytes": "45",
"transformationSummaries": [
{
"infoType": {
"name": "MY_INFO_TYPE_20210421_3"
},
"transformation": {
"replaceWithInfoTypeConfig": {}
},
"results": [
{
"count": "2",
"code": "SUCCESS"
}
],
"transformedBytes": "33"
},
{
"infoType": {
"name": "PERSON_NAME"
},
"transformation": {
"replaceWithInfoTypeConfig": {}
},
"results": [
{
"count": "1",
"code": "SUCCESS"
}
],
"transformedBytes": "12"
}
]
}
}
API 実行のためには、ここではサービスアカウントを利用しています。「DLP 管理者」ロールを付与しても動きますが、上記 API 実行のための最小の権限は以下のようになります。
Docker コンテナ内などで gcloud を利用していると、以下のようなエラーに遭遇することがあります。これは、NTP デーモンが動いておらず時刻がずれているときに発生します。
gcloud auth activate-service-account --key-file ./my-project-20210328-04084aee5ee7.json
ERROR: (gcloud.auth.activate-service-account) There was a problem refreshing your current auth tokens: ('invalid_grant: Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim.', '{"error":"invalid_grant","error_description":"Invalid JWT: Token must be a short-lived token (60 minutes) and in a reasonable timeframe. Check your iat and exp values in the JWT claim."}')
CentOS7 の場合は以下のようにして NTP デーモンをインストールして対処できます。
sudo yum -y install ntp
sudo systemctl start ntpd.service
サービスアカウントではなくユーザアカウントを用いている場合は、以下のようなエラーが出ます。
curl -sS -XPOST -H "Authorization: Bearer $(gcloud auth print-access-token)" -H 'Content-Type: applicaiton/json' https://dlp.googleapis.com/v2/projects/my-project-20210328/content:deidentify -d @dlp-req.json
{
"error": {
"code": 403,
"message": "Cloud Data Loss Prevention (DLP) API has not been used in project 32555940559 before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/dlp.googleapis.com/overview?project=32555940559 then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.",
"status": "PERMISSION_DENIED",
"details": [
{
"@type": "type.googleapis.com/google.rpc.Help",
"links": [
{
"description": "Google developers console API activation",
"url": "https://console.developers.google.com/apis/api/dlp.googleapis.com/overview?project=32555940559"
}
]
},
{
"@type": "type.googleapis.com/google.rpc.ErrorInfo",
"reason": "SERVICE_DISABLED",
"domain": "googleapis.com",
"metadata": {
"consumer": "projects/32555940559",
"service": "dlp.googleapis.com"
}
}
]
}
}
X-Goog-User-Project ヘッダを付与することで対処できます。
curl -sS -XPOST -H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H 'X-Goog-User-Project: my-project-20210328' \
-H 'Content-Type: applicaiton/json' https://dlp.googleapis.com/v2/projects/my-project-20210328/content:deidentify \
-d @dlp-req.json
上記エラーについて、インターネットで情報を調べていると Application Default Credentials (ADC) および gcloud auth application-default login
に関する記述が見つかります。
gcloud auth application-default login
は開発時などに用いることが想定された機能です。
サービスアカウントの key ファイルを使用する想定のアプリケーションがある場合に、ユーザアカウントの権限で同等のファイルを生成して、アプリケーションから利用させることができます。
gcloud auth application-default login
以下の認証ファイルが生成されます。
/home/vagrant/.config/gcloud/application_default_credentials.json
サービスアカウントの場合は以下のように環境変数を設定して、アプリケーションに場所を教える必要がありますが、gcloud auth application-default login
の場合は環境変数の設定は不要です。
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/my-project-20210328-04084aee5ee7.json
application_default_credentials.json
によって access token を生成することもできます。
curl -sS -XPOST -H "Authorization: Bearer $(gcloud auth application-default print-access-token)" \
-H 'X-Goog-User-Project: my-project-20210328' \
-H 'Content-Type: applicaiton/json' \
https://dlp.googleapis.com/v2/projects/my-project-20210328/content:deidentify \
-d @dlp-req.json
DLP のカスタム infoType には、Inspection テンプレートに内在するものと、そうでないものがあります。
Inspection テンプレートの外で定義できるカスタム infoType は「格納される infoType」と記載されています。
「単語A」および「単語B」に合致する箇所を検知する infoType と設定してみます。
Inspect テンプレートに対して、種類を「格納される infoType」とすることで紐付けることができます。「格納される infoType」の名前と、カスタム infoType の名前は同じである必要はありません。
Inspect テンプレートを De-Identification テンプレートで指定することで、確かに期待通りに動いていることが確認できます。
De-Identification で「格納される infoType」を指定するには以下のようにします。組み込みの infoType についても同様の設定が可能です。
指定していない infoType については、Inspection テンプレートが検知可能であっても、De-Identification テンプレートでは処理対象外となります。
Inspection テンプレートを指定しない場合は、DLP の既定の Inspection テンプレートが利用されることは前述のとおりです。その場合、既定の Inspection テンプレートは、「格納される infoType」を知らないため、以下のようなエラーとなります。
Inspection テンプレートにおける「検査ルールセット」を infoType に対して設定することで、特定の条件における検知を抑制することができます。
以下の例では「PERSON_NAME」infoType において「鈴木一郎」と「山田花子」を除外する設定となっています。
De-Identification テンプレートで確認すると、確かに期待通りに動いていることが分かります。
De-identification 処理のうち、Deterministic encryption using AES-SIV (CryptoDeterministicConfig
) と Format preserving encryption (CryptoReplaceFfxFpeConfig
) は、暗号化した結果を復号できます。
暗号化および復号処理するためには、DLP の De-Identification テンプレートに対称鍵を登録します。例えば、以下のようにして対称鍵を生成します。
128/192/256 bit (16/24/32 byte) size のキーを指定する必要があります。
python で 16バイトのキーを生成するためには以下のようにできます。ASCII 1 byte を 16 文字分です。
python -c "f = open('mykey.bin', 'wb'); f.write('1234567812345678')"
サイズ確認 2byte x 8
$ od mykey.bin
0000000 031061 032063 033065 034067 031061 032063 033065 034067
0000020
openssl コマンドでも乱数から生成できます。
openssl rand 16 > mykey2.bin
$ od mykey2.bin
0000000 172051 155300 161372 167002 036714 116604 034746 143713
0000020
上記の対称鍵を安全に GCP 上に保管するために、GCP KMS によって暗号化します。
KMS のキーは「キーリング」という鍵束の中に作成します。鍵束は以下のように作成します。
鍵束の中に鍵を作成します。
外部で作成した鍵をインポートすることもできますが、ここでは GCP 内で新規に対称鍵を生成することにします。簡単のため、鍵のローテーションも設定しません。
作成した KMS 鍵を用いて、DLP 内で利用したい対称鍵を暗号化します。
gcloud kms encrypt --location=global \
--keyring=my-keyring-20210423 \
--key=my-key-20210423 \
--plaintext-file=mykey2.bin \
--ciphertext-file=mykey2-kms-wrapped.bin
DLP に鍵を登録するためには、base64 でエンコードする必要があります。
cat mykey2-kms-wrapped.bin | base64 -w0 && echo
先程作成した KMS 鍵のリソース名もコピーしておきます。
De-Identification テンプレートの「変換ルール」のうち、復号処理をサポートしている Deterministic encryption using AES-SIV (CryptoDeterministicConfig
) を利用することにします。「仮名化 (暗号確定的トークン)」に該当します。
KMS キーのリソース名と、base64 エンコーディングした、KMS キーで暗号化 (ラップ) された対称鍵の二つを登録します。
更に「サロゲート infoType」というものも指定します。
正しく設定できていれば、以下のように変換されます。
API によって de-identification テンプレートを実行してみます。
dlp-req.json
{
"deidentifyConfig": {
},
"inspectConfig": {
},
"item": {
"value": "私の名前は田中一郎です。"
},
"inspectTemplateName": "projects/my-project-20210328/locations/global/inspectTemplates/my-inspection-template-20210421-3",
"deidentifyTemplateName": "projects/my-project-20210328/locations/global/deidentifyTemplates/my-deidentification-template-20210421-2"
}
実行例
curl -sS -XPOST -H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H 'X-Goog-User-Project: my-project-20210328' \
-H 'Content-Type: applicaiton/json' \
https://dlp.googleapis.com/v2/projects/my-project-20210328/content:deidentify \
-d @dlp-req.json
出力例
{
"item": {
"value": "私の名前はMY_SURROGATE(40):AYdLkcHd0L5YXbvQK2dLb0zIm8iuEwWNxyPCYXI=です。"
},
"overview": {
"transformedBytes": "12",
"transformationSummaries": [
{
"infoType": {
"name": "PERSON_NAME"
},
"transformation": {
"cryptoDeterministicConfig": {
"cryptoKey": {
"kmsWrapped": {
"wrappedKey": "CiQAPSZCCAD9MULuOWYswkN7tdUEDt7Waxm2Ab+rb4IXtcztMG4SOQAOuO8KqOFhhNhV3M4hmNVVB7OfjqYufpE0gDxiUQQrJMwd+dqcc3o84RP3TehcF76iU7xLwBFTCg==",
"cryptoKeyName": "projects/my-project-20210328/locations/global/keyRings/my-keyring-20210423/cryptoKeys/my-key-20210423"
}
},
"surrogateInfoType": {
"name": "MY_SURROGATE"
}
}
},
"results": [
{
"count": "1",
"code": "SUCCESS"
}
],
"transformedBytes": "12"
}
]
}
}
これを復号するためには、以下のようにします。
dlp-req2.json
{
"reidentifyConfig": {
},
"inspectConfig": {
"customInfoTypes": [
{
"infoType": { "name": "MY_SURROGATE" },
"surrogate_type": {}
}
]
},
"item": {
"value": "私の名前はMY_SURROGATE(40):AYdLkcHd0L5YXbvQK2dLb0zIm8iuEwWNxyPCYXI=です。"
},
"reidentifyTemplateName": "projects/my-project-20210328/locations/global/deidentifyTemplates/my-deidentification-template-20210421-2"
}
実行例
curl -sS -XPOST -H "Authorization: Bearer $(gcloud auth print-access-token)" \
-H 'X-Goog-User-Project: my-project-20210328' \
-H 'Content-Type: applicaiton/json' \
https://dlp.googleapis.com/v2/projects/my-project-20210328/content:reidentify \
-d @dlp-req2.json
確かに復号できたことが確認できます。
{
"item": {
"value": "私の名前は田中一郎です。"
},
"overview": {
"transformedBytes": "57",
"transformationSummaries": [
{
"infoType": {
"name": "MY_SURROGATE"
},
"transformation": {
"cryptoDeterministicConfig": {
"cryptoKey": {
"kmsWrapped": {
"wrappedKey": "CiQAPSZCCAD9MULuOWYswkN7tdUEDt7Waxm2Ab+rb4IXtcztMG4SOQAOuO8KqOFhhNhV3M4hmNVVB7OfjqYufpE0gDxiUQQrJMwd+dqcc3o84RP3TehcF76iU7xLwBFTCg==",
"cryptoKeyName": "projects/my-project-20210328/locations/global/keyRings/my-keyring-20210423/cryptoKeys/my-key-20210423"
}
},
"surrogateInfoType": {
"name": "MY_SURROGATE"
}
}
},
"results": [
{
"count": "1",
"code": "SUCCESS"
}
],
"transformedBytes": "57"
}
]
}
}
REST API ではなく python SDK を用いた例はこちらのページに記載があります。
dlp-req2.json で指定した customInfoTypes
のリファレンスはこちらです。
Re-Identification API のリファレンスはこちらです。
De-Identification API と非常に良く似ていますが、inspectConfig
に customInfoTypes
として「サロゲート infoType」を指定する必要があることに注意します。検知できないと Re-Identification 処理できないためです。
DLP API dlp.googleapis.com
は VPC-SC の内部に閉じ込めることができます。詳細についてはこちらです。