メインコンテンツへスキップ

Langfuseにおける個人情報(PII)マスキング:Sensitive Data Protection の活用

著者
Hiromi Kuwa

これまで、LangfuseでのPIIマスキング手法として、llm-guardGuardrails for Amazon Bedrock 、そしてLLM(Gemini 2.5 Flash Lite) によるマスキング手法を検討してきました。

今回は、Google Cloudの機密データ保護機能であるSensitive Data Protection(旧:Cloud Data Loss Prevention, Cloud DLP) の利用について検討します。

Sensitive Data Protection とは?
#

Sensitive Data Protection (旧称:Cloud Data Loss Prevention、Cloud DLP)は、Google Cloudが提供する機密データ保護サービスです。データの検出、分類、匿名化といった強力な機能を提供し、個人情報(PII)をはじめとする機密情報の漏洩リスクを軽減します。

このサービスは、単なるパターンマッチングにとどまらず、機械学習や文脈分析を組み合わせることで、より高精度な機密データ検出を実現します。特に、クレジットカード番号、社会保障番号、メールアドレス、電話番号など、世界中の150種類以上の組み込みのInfoType(情報タイプ) 検出器をサポートしており、非常に広範な種類のPIIに対応できます。また、独自のビジネスニーズに合わせてカスタムInfoTypeを定義することも可能です。

Guardrails for Amazon Bedrockと同様に、APIを通じてPIIマスキングを実行できます。今回の検証では、DLP APIを利用してマスキングを行います。

Guardrails for Amazon Bedrock との違いは?
#

これまでの検証で取り上げたGuardrails for Amazon Bedrockは、Sensitive Data Protection と目的が近いサービスです。どちらもLLMの入出力におけるPIIマスキングに利用できますが、いくつか重要な違いがあります。

特徴 \ ソリューションSensitive Data ProtectionGuardrails for Amazon Bedrock
最適なユーザーGoogle Cloud環境でLLMアプリケーションを運用するユーザーAWS BedrockをLLMプラットフォームとして利用するユーザー
検出範囲と精度汎用PII検出、150種類以上の高精度なInfoType(PIIに特化)Bedrockの入出力に特化、PIIを含む多様なフィルタタイプ
適用レイヤーアプリケーション層での柔軟な適用(API経由)Bedrock API呼び出し時(リアルタイム)
リアルタイム性API経由でリアルタイム処理が可能リアルタイムで適用可能
Langfuse連携API呼び出し結果をLangfuseへ記録可能API呼び出し結果をLangfuseへ記録可能
その他特徴高度な匿名化オプション、広範なPII検出器、コンテキストを考慮した検出Bedrockでのコンテンツモデレーションの一元化、リアルタイムポリシー適用

Guardrails for Amazon BedrockはAmazon Bedrockと密接に統合されており、Bedrockを利用する際に特化したリアルタイム制御を提供します。一方、Sensitive Data ProtectionはGoogle Cloudの汎用的な機密データ保護サービスであり、より広範なPIIタイプに対応し、アプリケーション層で柔軟に組み込める点が特徴です。

今回の検証では、PII検出の対象をサンプルコード内で設定しますが、Guardrailsと同様にコンソール上での設定も可能です。サービスとして運用する際は、コンソールで検出対象を追加・調整し、アプリケーションの修正やリリースなしでポリシーを更新できるテンプレートの利用を検討することをおすすめします。

テスト方法
#

今回は以下の手順で、Sensitive Data ProtectionのPIIマスキング機能を検証していきます。

事前準備
#

まず、Sensitive Data Protection が提供するInfoType検出器(検出対象) を確認します。検出器のリファレンスはこちら にあります。過去の検証と同様に、今回はカスタム検出器は利用せず、組み込みのInfoType検出器のみを使用します。

当初、すべてのInfoTypeを設定しようとしましたが、検出器の設定可能上限数(150個)を超過したため(総数216個)、カテゴリがPIIに該当するものに限定して設定することにしました。

多数のInfoTypeを手動でリスト化するのは大変なので、以下のPythonコードでAPIからPII関連のInfoTypeを自動で取得します。

# DLPクライアントを初期化(クォータプロジェクトを指定)
dlp_client = dlp_v2.DlpServiceClient
    client_options={"quota_project_id": project_id}
)

def get_info_types():
    info_types_response = dlp_client.list_info_types(
        request={
            'parent': f"projects/{project_id}",
            'filter': 'supported_by=INSPECT' # 検査可能なInfoTypeに絞る
        }
    )

    included_info_types = []
    pii_list = []
    for info_type in info_types_response.info_types:
        if info_type.name in included_info_types:
            continue

        # categoriesを反復してPIIを探す
        is_pii = False
        for category in info_type.categories:
            if category.type_category.name == "PII":
                is_pii = True
                break;

        if is_pii:
            included_info_types.extend(info_type.specific_info_types)
            pii_list.append({
                'name': info_type.name,
            })

    return pii_list

参考
#

マスキング対象として期待する項目と、llm-guardのAnonymize機能、Guardrails for Amazon Bedrockの機密情報フィルター、そしてSensitive Data ProtectionのInfoType検出器で対応すると思われる項目を比較表にまとめました。

PIIllm-guardGuardrail for Amazon BedrockSensitive Data Protection
氏名人名NAMEPERSON_NAME
生年月日--DATE_OF_BIRTH
住所-ADDRESSGEOGRAPHIC_DATA
電話番号電話番号PHONEPHONE_NUMBER
メールアドレスメールアドレスEMAILEMAIL_ADDRESS
運転免許証番号-DRIVER_IDDRIVERS_LICENSE_NUMBER
パスポート番号-US_PASSPORT_NUMBERPASSPORT
社会保障番号米国社会保障番号(SSN)US_SOCIAL_SECURITY_NUMBERUS_SOCIAL_SECURITY_NUMBER JAPAN_INDIVIDUAL_NUMBER(マイナンバー)
(クレジットカード情報) カード番号クレジットカードCREDIT_DEBIT_CARD_NUMBERCREDIT_CARD_DATA
(クレジットカード情報) 有効期限-CREDIT_DEBIT_CARD_EXPIRYCREDIT_CARD_DATA
(クレジットカード情報) セキュリティコード-CREDIT_DEBIT_CARD_CVVCREDIT_CARD_DATA
(銀行口座情報)銀行名---
(銀行口座情報)支店名---
(銀行口座情報) 口座番号-US_BANK_ROUTING_NUMBERFINANCIAL_ID

llm-guardと比べると検出項目は充実していますが、Guardrails for Amazon Bedrockでカバーしきれていなかった「生年月日」もSensitive Data Protectionでは検出できるようです。銀行口座情報の銀行名や支店名を除けば、期待されるほとんどのPII項目を網羅できる見込みです。

今回の検証では、誤検知が発生しないかも確認するため、PIIカテゴリに属する85種類の検出器をすべて設定しました。

アプリケーションコード
#

過去の記事と同様に、簡単なPythonコードを用いてプロンプトの入力とLLMの回答を模擬的に生成し、それぞれをLangfuseに登録する形式でテストを行います。

get_info_types関数とDLPクライアントの初期化部分を追記、masking_functionの中身をDLP APIを利用するように修正します。マスキング方法は、今回は検出されたInfoTypeの名前で置換する設定にします。

今回は行いませんが、「*」のような特定の文字で置き換えたり、固定文言の指定、設定した文言のいずれかと置換、といった設定も可能です。


def masking_function(data: any, **kwargs) -> any:
    if isinstance(data, str) and len(data) > 0:
        # 利用可能なPIIのInfoTypeを取得
        info_types_list = get_info_types()

        request = {
                    'parent': f"projects/{project_id}",
                    'inspect_config': {
                        'info_types': info_types_list
                    },
                    'deidentify_config': {
                        'info_type_transformations': {
                            'transformations': [
                                {
                                    'primitive_transformation': {
                                        'replace_with_info_type_config': {}
                                    }
                                }
                            ]
                        }
                    },
                    'item': {'value': data}
                }

        # DLPを使用したPIIマスキング
        response = dlp_client.deidentify_content(
                request=request
        )

        sanitized_data = response.item.value
        return sanitized_data
    else:
        return data

テストデータ
#

過去の記事と同様に、以下のダミーデータを流用してテストを行いました。

ダミーデータ

私の個人情報は以下の通りです:

氏名:山田 太郎
Name: Taro Yamada

生年月日:1985年3月15日
Birthday: March 15, 1985

住所:東京都新宿区西新宿2-8-1
Address: 2-8-1 Nishi-Shinjuku, Shinjuku-ku, Tokyo, Japan

電話番号:03-1234-5678 / 090-9999-8888
Phone: +81-3-1234-5678 / +81-90-9999-8888

メールアドレス:taro.yamada@example.com
Email: taro.yamada@example.com

運転免許証番号:123456789012
Driver's License: DL-123456789012

パスポート番号:TK1234567
Passport Number: TK1234567

社会保障番号:987-65-4321
Social Security Number: 987-65-4321

クレジットカード情報:
- カード番号:4111-2222-3333-4444
- 有効期限:12/25
- セキュリティコード:123

Credit Card Information:
- Card Number: 4111-2222-3333-4444
- Expiry Date: 12/25
- Security Code: 123

銀行口座情報:
- 銀行名:みずほ銀行
- 支店名:新宿支店
- 口座番号:1234567

Bank Account Information:
- Bank Name: Mizuho Bank
- Branch: Shinjuku Branch
- Account Number: 1234567

検証結果
#

以下に、llm-guard(デフォルト設定)、Guardrails for Amazon Bedrock(apply_guardrail)、そして今回検証したSensitive Data Protectionそれぞれで処理した際のPII項目ごとの置換結果を比較します。

llm-guard
(デフォルト設定)
Guardrail for Amazon BedrockSensitive Data Protection
氏名(日本語)[REDACTED_PERSON_1][REDACTED_PERSON_2][REDACTED_PERSON_3]{NAME}[PERSON_NAME]
氏名(英語)[REDACTED_PERSON_4]{NAME}[PERSON_NAME]
生年月日--[DATE_OF_BIRTH]
住所-{ADDRESS}[PERSON_NAME][GEOGRAPHIC_DATA]
電話番号(日本)03-1234-[REDACTED_PHONE_NUMBER_1]{PHONE}[PHONE_NUMBER] / [PHONE_NUMBER]
電話番号(海外)[REDACTED_PHONE_NUMBER_2]{PHONE}[PHONE_NUMBER] / [PHONE_NUMBER]
メールアドレス[REDACTED_EMAIL_ADDRESS_1]{EMAIL}[EMAIL_ADDRESS]
運転免許証番号-{DRIVER_ID}[DRIVERS_LICENSE_NUMBER]
DL-[DRIVERS_LICENSE_NUMBER]
パスポート番号-{US_PASSPORT_NUMBER}パスポート番号:[DRIVERS_LICENSE_NUMBER]
Passport Number: [PASSPORT]
社会保障番号[REDACTED_US_SSN_RE_1]987-65-4321
{US_SOCIAL_SECURITY_NUMBER}
987-65-4321
クレジットカード(カード番号)[REDACTED_CREDIT_CARD_RE_1]{CREDIT_DEBIT_CARD_NUMBER}4111-2222-3333-4444
クレジットカード(カード番号以外)-有効期限:{CREDIT_DEBIT_CARD_EXPIRY}
セキュリティコード:{CREDIT_DEBIT_CARD_CVV}
有効期限:12/25
セキュリティコード:123
銀行口座情報-銀行名:みずほ銀行
支店名:新宿支店
口座番号:{US_BANK_ACCOUNT_NUMBER}

Bank Name: Mizuho Bank
Branch: {ADDRESS} Branch
Account Number: {US_BANK_ACCOUNT_NUMBER}
銀行名:[PERSON_NAME]銀行
支店名:[GEOGRAPHIC_DATA]
口座番号:[FINANCIAL_ID]

Bank Name: Mizuho Bank
Branch: Shinjuku Branch
Account Number: [FINANCIAL_ID]

llm-guard(デフォルト設定)とGuardrails for Amazon Bedrock(apply_guardrail)の結果は、以前の記事で検証した際のデータを記載しています。Sensitive Data Protection の検証において、上記2回と明示的に差異がある項目の背景色を変更しています。

今回の検証で目立つ点としては、期待していたクレジットカード情報や社会保障番号が検出されませんでした。また、いくつかの誤検出も見られます。DLPの検出器には、コンテキストの手がかりがある場合にのみ識別できるものもあるため、もう少し自然な文脈を与えたテキストであれば、結果は変わってくるかもしれません。

結果詳細(誤検出・検出漏れ項目の抜粋)

氏名
#

氏名:[PERSON_NAME]Name: [PERSON_NAME]

検出自体は問題なさそうですが、元のテキストの改行が消えてしまっています。

住所
#

住所:[PERSON_NAME][GEOGRAPHIC_DATA]

何かが人名として誤検出されてしまっています。また、どこまでが人名として検出されているかは不明なため推測にはなりますが、おそらく日本語住所+改行+Address:+英語住所がひとまとめに住所としてみなされているようです。

運転免許証番号
#

運転免許証番号:[DRIVERS_LICENSE_NUMBER] Driver’s License: DL-[DRIVERS_LICENSE_NUMBER]

誤検出というほどではありませんが、「DL-」というプレフィックスが残ってしまっています。

パスポート番号
#

パスポート番号:[DRIVERS_LICENSE_NUMBER] Passport Number: [PASSPORT]

日本語版が運転免許証番号として誤検出されています。

社会保障番号
#

社会保障番号:987-65-4321 Social Security Number: 987-65-4321

期待とはことなり、PIIとして検出されませんでした。

クレジットカード情報
#

クレジットカード情報:

  • カード番号:4111-2222-3333-4444
  • 有効期限:12/25
  • セキュリティコード:123

Credit Card Information:

  • Card Number: 4111-2222-3333-4444
  • Expiry Date: [CREDIT_CARD_EXPIRATION_DATE]
  • Security Code: 123

英語版の有効期限のみが検出されました。

銀行口座情報
#

銀行口座情報:

  • 銀行名:[PERSON_NAME]銀行
  • 支店名:[GEOGRAPHIC_DATA]
  • 口座番号:[FINANCIAL_ID]

Bank Account Information:

  • Bank Name: Mizuho Bank
  • Branch: Shinjuku Branch
  • Account Number: [JAPAN_BANK_ACCOUNT]

日本語の銀行名が個人名、支店名が住所として誤検出されています。

FINANCIAL_ID に JAPAN_BANK_ACCOUNT は含有されていますが、異なる検出器で置き換わっているのは気になります。

検出の精度は十分とは言えませんが、Guardrailsやllm-guardでは検出できなかった一部の項目(例:生年月日)を捉えることができました。

まとめ
#

今回はGoogle CloudのSensitive Data Protectionを利用したPIIマスキング機能について検証しました。

テストの結果、一部の項目で優れた検出能力を示しましたが、完全に網羅できているわけではなく、誤検出も見られました。今回の検証で検出できなかったPII項目の一部は、llm-guardのような別のフィルタリングツールでカバーできる可能性があります。そのため、Sensitive Data Protectionとllm-guardを併用することで、コストを抑えつつカバー範囲を広げられるかもしれません。

また、今回の検証は限定的な条件での確認となります。実際にアプリケーションで利用する際は、自然なテキスト中にPIIが含まれている場合や、全角文字が混在する場合など、日本語特有の表現が含まれている場合の挙動については、さらに詳細な検証が必要不可欠です。

検討課題
#

Sensitive Data Protectionの料金体系 も考慮すべき点です。料金は、検出・変換されたデータ量(バイト数)に基づいて発生します。月間1GBまでの利用は無料ですが、それを超えると1GBあたり2~3 USDの費用がかかるため、他のソリューションと比較して高額になる可能性があります。