変換のリファレンス

このトピックでは、Cloud DLP で利用できる匿名化のトークン化手法(変換)について説明します。

トークン化手法の種類

匿名化されるデータの種類と匿名化の目的によって、使用するトークン化変換は異なります。Cloud DLP がサポートするトークン化手法は、次の一般的なカテゴリに分類されます。

  • 削除: 検出された機密値のすべてまたは一部を削除します。
  • 置換: 検出された機密値を指定されたサロゲート値に置換します。
  • マスキング: 機密値の文字数をハッシュ(#)やアスタリスク(*)などの指定された代替文字に置き換えます。
  • 暗号ベースのトークン化: 暗号鍵を使用して元の機密値を暗号化します。Cloud DLP は、逆行または「再識別」できる変換を含む数種類のトークン化をサポートしています。
  • バケット化: 機密値をある範囲の値に置き換えることによって「一般化」します(たとえば、特定の年齢を年齢の範囲に、気温を「暑い」「ふつう」「寒い」に対応する範囲に置き換えます)。
  • 日付シフト: 機密の日付値をランダムな日数だけシフトします。
  • 時間抽出: 日付と時刻の値の指定部分を抽出または保存します。

このトピックの残りの部分では、それぞれ異なるタイプのトークン化変換とその使用例について説明します。

変換方法

次の表は、Cloud DLP で提供されている機密データの匿名化のためのトークン化変換の一覧を示しています。

変換 オブジェクト 説明 逆行可能性1 参照整合性2 入力タイプ
削除 RedactConfig 値を削除します。 任意
置換 ReplaceValueConfig 入力値を特定の値に置換します。 任意
infoType での置換 ReplaceWithInfoTypeConfig infoType の名前で入力値を置換します。 任意
文字でマスキング CharacterMaskConfig 指定された固定文字で特定の文字数を置換し、文字列全体または一部をマスキングします。 任意
仮名化(入力値を暗号ハッシュで置換) CryptoHashConfig 特定のデータ暗号鍵で生成された 32 バイトの 16 進文字列で入力値を置換します。詳細については、仮名化のコンセプトのドキュメントをご覧ください。 文字列か整数
仮名化(暗号形式を維持したトークンに置換) CryptoReplaceFfxFpeConfig FFX モードのフォーマット保持暗号化(FPE)を使用して、入力値を同じ長さの「トークン」(サロゲート値)に置き換えます。これにより、実際の情報が明らかにされていなくても、フォーマットの検証が必要なシステム、または実際の情報として表示する必要があるシステムで出力を使用できます。詳細については、仮名化のコンセプトのドキュメントをご覧ください。 制限された文字数の文字列か整数。アルファベットは 2 文字以上、62 文字以下にする必要があります。
仮名化(暗号トークンに置換) CryptoDeterministicConfig 合成初期化ベクトルモード(AES-SIV)の AES を使用して、入力値を同じ長さのトークンまたはサロゲート値に置換します。この変換方法は、形式を維持するトークン化とは異なり、サポートされている文字列セットに制限がなく、同じ入力値のインスタンスごとに同じトークンを生成し、[surrogates] (reference/rest/v2/InspectConfig#surrogatetype) を使用して元の暗号鍵に基づいて再識別ができます。 任意
固定サイズの範囲に基づくバケット値 FixedSizeBucketingConfig バケットまたは入力値が存在する範囲で入力値を置換してマスキングします。 任意
カスタムサイズの範囲に基づくバケット値 BucketingConfig ユーザーが構成可能な範囲と置換値に基づいたバケット入力値。 任意
日付シフト DateShiftConfig コンテキストの一貫性を維持しながら、任意の日数で日付をシフトします。
順序と持続時間を保持
日付 / 時刻
時間データの抽出 TimePartConfig DateTimestampTimeOfDay 値の部分を抽出または保持します。 日付 / 時刻

脚注

1 content.reidentify メソッドを使用して機密データを再識別するために、元に戻すことが可能な変換を使用できます。
2 参照整合性により、データを匿名化しながら、レコード同士の関係を維持できます。たとえば、同じ暗号鍵とコンテキストの場合、データが変換されるたびに同じ難読化された形式に置き換えられるため、レコード間の接続を保持できます。

削除

機密データを入力コンテンツから単純に削除するには、Cloud DLP の削除変換(DLP API の RedactConfig)を使用します。

たとえば、EMAIL_ADDRESS というすべての infoType の単純削除を行う場合、次の文字列が Cloud DLP に送信されます。

My name is Alicia Abernathy, and my email address is aabernathy@example.com.

次の文字列が返されます。

My name is Alicia Abernathy, and my email address is .

次の JSON の例は、API リクエストの作成方法と、Cloud DLP API の戻り値を示しています。

JSON 入力:

POST https://dlp.googleapis.com/v2/projects/[PROJECT_ID]/content:deidentify?key={YOUR_API_KEY}

{
  "item":{
    "value":"My name is Alicia Abernathy, and my email address is aabernathy@example.com."
  },
  "deidentifyConfig":{
    "infoTypeTransformations":{
      "transformations":[
        {
          "infoTypes":[
            {
              "name":"EMAIL_ADDRESS"
            }
          ],
          "primitiveTransformation":{
            "redactConfig":{

            }
          }
        }
      ]
    }
  },
  "inspectConfig":{
    "infoTypes":[
      {
        "name":"EMAIL_ADDRESS"
      }
    ]
  }
}

JSON 出力:

{
  "item":{
    "value":"My name is Alicia Abernathy, and my email address is ."
  },
  "overview":{
    "transformedBytes":"22",
    "transformationSummaries":[
      {
        "infoType":{
          "name":"EMAIL_ADDRESS"
        },
        "transformation":{
          "redactConfig":{

          }
        },
        "results":[
          {
            "count":"1",
            "code":"SUCCESS"
          }
        ],
        "transformedBytes":"22"
      }
    ]
  }
}

置換

置換変換は、各入力値を特定のトークン値か、その infoType 名のいずれかに置き換えます。

基本置換

基本置換変換(DLP API の ReplaceValueConfig)では、検出された機密データ値が指定された値で置き換えられます。たとえば、検出された EMAIL_ADDRESS という infoType をすべて "[fake@example.com]" に置換するように指定した場合、次の文字列が Cloud DLP に送信されます。

My name is Alicia Abernathy, and my email address is aabernathy@example.com.

次の文字列が返されます。

My name is Alicia Abernathy, and my email address is [fake@example.com].

次の JSON の例は、API リクエストの作成方法と、Cloud DLP API の戻り値を示しています。

JSON 入力:

POST https://dlp.googleapis.com/v2/projects/[PROJECT_ID]/content:deidentify?key={YOUR_API_KEY}

{
  "item":{
    "value":"My name is Alicia Abernathy, and my email address is aabernathy@example.com."
  },
  "deidentifyConfig":{
    "infoTypeTransformations":{
      "transformations":[
        {
          "infoTypes":[
            {
              "name":"EMAIL_ADDRESS"
            }
          ],
          "primitiveTransformation":{
            "replaceConfig":{
              "newValue":{
                "stringValue":"[email-address]"
              }
            }
          }
        }
      ]
    }
  },
  "inspectConfig":{
    "infoTypes":[
      {
        "name":"EMAIL_ADDRESS"
      }
    ]
  }
}

JSON 出力:

{
  "item":{
    "value":"My name is Alicia Abernathy, and my email address is [email-address]."
  },
  "overview":{
    "transformedBytes":"22",
    "transformationSummaries":[
      {
        "infoType":{
          "name":"EMAIL_ADDRESS"
        },
        "transformation":{
          "replaceConfig":{
            "newValue":{
              "stringValue":"[email-address]"
            }
          }
        },
        "results":[
          {
            "count":"1",
            "code":"SUCCESS"
          }
        ],
        "transformedBytes":"22"
      }
    ]
  }
}

infoType の置換

infoType の置換を指定することもできます(DLP API の ReplaceWithInfoTypeConfig)。この変換では、基本置換変換と同じ操作が行われたうえで、検出された各機密データ値が検出値の infoType に置換されます。

たとえば、メールアドレスと姓の両方を検出し、検出されたそれぞれの値をその値の infoType に置換するように指定した場合、次の文字列が Cloud DLP に送信されます。

My name is Alicia Abernathy, and my email address is aabernathy@example.com.

次の文字列が返されます。

My name is Alicia LAST_NAME, and my email address is EMAIL_ADDRESS.

マスキング

各文字をアスタリスク(*)やハッシュ(#)などの固定された単一のマスキング文字に置換することで、検出された機密値(DLP API の CharacterMaskConfig)を完全または部分的にマスキングするように Cloud DLP を構成できます。マスキングは、文字列の先頭または末尾から開始できます。この変換は、長整数などの数値型にも対応しています。

Cloud DLP のマスキング変換には、以下のオプションを指定できます。

  • マスキング文字(DLP API の maskingCharacter 引数): 機密値の各文字のマスキングに使用する文字。たとえば、アスタリスク(*)、ドル($)でクレジット カード番号などの一連の数字をマスキングできます。
  • マスクする文字数(numberToMask): この値を指定しない場合、すべての文字がマスクされます。
  • 逆順にするかどうか(reverseOrder): 文字を逆順にマスキングするかどうか。逆順にすると、一致した値の文字は値の終わりから始まりに向かってマスキングされます。
  • 無視する文字(charactersToIgnore): 値のマスキングの際にスキップする 1 つ以上の文字。たとえば、電話番号をマスキングする際に、ハイフンをそのままにするよう指定できます。また、マスキングする際に無視する共通の文字グループ(CharsToIgnore)も指定できます。

メールアドレスに文字マスキング変換を使用するように指定する場合、次の文字列を Cloud DLP に送信します。

My name is Alicia Abernathy, and my email address is aabernathy@example.com.

マスキング文字が「#」に送信され、無視する文字が共通の文字セットに設定されて、その他がデフォルト設定の場合、Cloud DLP は次の文字列を返します。

My name is Alicia Abernathy, and my email address is ##########@#######.###.

次の JSON とサンプルコードは、マスキング変換の仕組みを示しています。

プロトコル

JSON 入力:

POST https://dlp.googleapis.com/v2/projects/[PROJECT_ID]/content:deidentify?key={YOUR_API_KEY}

{
  "item":{
    "value":"My name is Alicia Abernathy, and my email address is aabernathy@example.com."
  },
  "deidentifyConfig":{
    "infoTypeTransformations":{
      "transformations":[
        {
          "infoTypes":[
            {
              "name":"EMAIL_ADDRESS"
            }
          ],
          "primitiveTransformation":{
            "characterMaskConfig":{
              "maskingCharacter":"#",
              "reverseOrder":false,
              "charactersToIgnore":[
                {
                  "charactersToSkip":".@"
                }
              ]
            }
          }
        }
      ]
    }
  },
  "inspectConfig":{
    "infoTypes":[
      {
        "name":"EMAIL_ADDRESS"
      }
    ]
  }
}

JSON 出力:

{
  "item":{
    "value":"My name is Alicia Abernathy, and my email address is ##########@#######.###."
  },
  "overview":{
    "transformedBytes":"22",
    "transformationSummaries":[
      {
        "infoType":{
          "name":"EMAIL_ADDRESS"
        },
        "transformation":{
          "characterMaskConfig":{
            "maskingCharacter":"#",
            "charactersToIgnore":[
              {
                "charactersToSkip":".@"
              }
            ]
          }
        },
        "results":[
          {
            "count":"1",
            "code":"SUCCESS"
          }
        ],
        "transformedBytes":"22"
      }
    ]
  }
}

Java

/**
 * Deidentify a string by masking sensitive information with a character using the DLP API.
 *
 * @param string The string to deidentify.
 * @param maskingCharacter (Optional) The character to mask sensitive data with.
 * @param numberToMask (Optional) The number of characters' worth of sensitive data to mask.
 *     Omitting this value or setting it to 0 masks all sensitive chars.
 * @param projectId ID of Google Cloud project to run the API under.
 */
private static void deIdentifyWithMask(
    String string,
    List<InfoType> infoTypes,
    Character maskingCharacter,
    int numberToMask,
    String projectId) {

  // instantiate a client
  try (DlpServiceClient dlpServiceClient = DlpServiceClient.create()) {

    ContentItem contentItem = ContentItem.newBuilder().setValue(string).build();

    CharacterMaskConfig characterMaskConfig =
        CharacterMaskConfig.newBuilder()
            .setMaskingCharacter(maskingCharacter.toString())
            .setNumberToMask(numberToMask)
            .build();

    // Create the deidentification transformation configuration
    PrimitiveTransformation primitiveTransformation =
        PrimitiveTransformation.newBuilder().setCharacterMaskConfig(characterMaskConfig).build();

    InfoTypeTransformation infoTypeTransformationObject =
        InfoTypeTransformation.newBuilder()
            .setPrimitiveTransformation(primitiveTransformation)
            .build();

    InfoTypeTransformations infoTypeTransformationArray =
        InfoTypeTransformations.newBuilder()
            .addTransformations(infoTypeTransformationObject)
            .build();

    InspectConfig inspectConfig =
        InspectConfig.newBuilder()
            .addAllInfoTypes(infoTypes)
            .build();

    DeidentifyConfig deidentifyConfig =
        DeidentifyConfig.newBuilder()
            .setInfoTypeTransformations(infoTypeTransformationArray)
            .build();

    // Create the deidentification request object
    DeidentifyContentRequest request =
        DeidentifyContentRequest.newBuilder()
            .setParent(ProjectName.of(projectId).toString())
            .setInspectConfig(inspectConfig)
            .setDeidentifyConfig(deidentifyConfig)
            .setItem(contentItem)
            .build();

    // Execute the deidentification request
    DeidentifyContentResponse response = dlpServiceClient.deidentifyContent(request);

    // Print the character-masked input value
    // e.g. "My SSN is 123456789" --> "My SSN is *********"
    String result = response.getItem().getValue();
    System.out.println(result);
  } catch (Exception e) {
    System.out.println("Error in deidentifyWithMask: " + e.getMessage());
  }
}

Node.js

// Imports the Google Cloud Data Loss Prevention library
const DLP = require('@google-cloud/dlp');

// Instantiates a client
const dlp = new DLP.DlpServiceClient();

// The project ID to run the API call under
// const callingProjectId = process.env.GCLOUD_PROJECT;

// The string to deidentify
// const string = 'My SSN is 372819127';

// (Optional) The maximum number of sensitive characters to mask in a match
// If omitted from the request or set to 0, the API will mask any matching characters
// const numberToMask = 5;

// (Optional) The character to mask matching sensitive data with
// const maskingCharacter = 'x';

// Construct deidentification request
const item = {value: string};
const request = {
  parent: dlp.projectPath(callingProjectId),
  deidentifyConfig: {
    infoTypeTransformations: {
      transformations: [
        {
          primitiveTransformation: {
            characterMaskConfig: {
              maskingCharacter: maskingCharacter,
              numberToMask: numberToMask,
            },
          },
        },
      ],
    },
  },
  item: item,
};

try {
  // Run deidentification request
  const [response] = await dlp.deidentifyContent(request);
  const deidentifiedItem = response.item;
  console.log(deidentifiedItem.value);
} catch (err) {
  console.log(`Error in deidentifyWithMask: ${err.message || err}`);
}

Python

def deidentify_with_mask(project, string, info_types, masking_character=None,
                         number_to_mask=0):
    """Uses the Data Loss Prevention API to deidentify sensitive data in a
    string by masking it with a character.
    Args:
        project: The Google Cloud project id to use as a parent resource.
        item: The string to deidentify (will be treated as text).
        masking_character: The character to mask matching sensitive data with.
        number_to_mask: The maximum number of sensitive characters to mask in
            a match. If omitted or set to zero, the API will default to no
            maximum.
    Returns:
        None; the response from the API is printed to the terminal.
    """

    # Import the client library
    import google.cloud.dlp

    # Instantiate a client
    dlp = google.cloud.dlp.DlpServiceClient()

    # Convert the project id into a full resource id.
    parent = dlp.project_path(project)

    # Construct inspect configuration dictionary
    inspect_config = {
        'info_types': [{'name': info_type} for info_type in info_types]
    }

    # Construct deidentify configuration dictionary
    deidentify_config = {
        'info_type_transformations': {
            'transformations': [
                {
                    'primitive_transformation': {
                        'character_mask_config': {
                            'masking_character': masking_character,
                            'number_to_mask': number_to_mask
                        }
                    }
                }
            ]
        }
    }

    # Construct item
    item = {'value': string}

    # Call the API
    response = dlp.deidentify_content(
        parent, inspect_config=inspect_config,
        deidentify_config=deidentify_config, item=item)

    # Print out the results.
    print(response.item.value)

Go

// mask deidentifies the input by masking all provided info types with maskingCharacter
// and prints the result to w.
func mask(w io.Writer, client *dlp.Client, project, input string, infoTypes []string, maskingCharacter string, numberToMask int32) {
	// Convert the info type strings to a list of InfoTypes.
	var i []*dlppb.InfoType
	for _, it := range infoTypes {
		i = append(i, &dlppb.InfoType{Name: it})
	}
	// Create a configured request.
	req := &dlppb.DeidentifyContentRequest{
		Parent: "projects/" + project,
		InspectConfig: &dlppb.InspectConfig{
			InfoTypes: i,
		},
		DeidentifyConfig: &dlppb.DeidentifyConfig{
			Transformation: &dlppb.DeidentifyConfig_InfoTypeTransformations{
				InfoTypeTransformations: &dlppb.InfoTypeTransformations{
					Transformations: []*dlppb.InfoTypeTransformations_InfoTypeTransformation{
						{
							InfoTypes: []*dlppb.InfoType{}, // Match all info types.
							PrimitiveTransformation: &dlppb.PrimitiveTransformation{
								Transformation: &dlppb.PrimitiveTransformation_CharacterMaskConfig{
									CharacterMaskConfig: &dlppb.CharacterMaskConfig{
										MaskingCharacter: maskingCharacter,
										NumberToMask:     numberToMask,
									},
								},
							},
						},
					},
				},
			},
		},
		// The item to analyze.
		Item: &dlppb.ContentItem{
			DataItem: &dlppb.ContentItem_Value{
				Value: input,
			},
		},
	}
	// Send the request.
	r, err := client.DeidentifyContent(context.Background(), req)
	if err != nil {
		log.Fatal(err)
	}
	// Print the result.
	fmt.Fprint(w, r.GetItem().GetValue())
}

PHP

use Google\Cloud\Dlp\V2\CharacterMaskConfig;
use Google\Cloud\Dlp\V2\DlpServiceClient;
use Google\Cloud\Dlp\V2\InfoType;
use Google\Cloud\Dlp\V2\PrimitiveTransformation;
use Google\Cloud\Dlp\V2\DeidentifyConfig;
use Google\Cloud\Dlp\V2\InfoTypeTransformations\InfoTypeTransformation;
use Google\Cloud\Dlp\V2\InfoTypeTransformations;
use Google\Cloud\Dlp\V2\ContentItem;

/**
 * Deidentify sensitive data in a string by masking it with a character.
 * @param string $callingProjectId The GCP Project ID to run the API call under
 * @param string $string The string to deidentify
 * @param int $numberToMask (Optional) The maximum number of sensitive characters to mask in a match
 * @param string $maskingCharacter (Optional) The character to mask matching sensitive data with
 */
function deidentify_mask(
  $callingProjectId,
  $string,
  $numberToMask = 0,
  $maskingCharacter = 'x'
) {
    // Instantiate a client.
    $dlp = new DlpServiceClient();

    // The infoTypes of information to mask
    $ssnInfoType = (new InfoType())
        ->setName('US_SOCIAL_SECURITY_NUMBER');
    $infoTypes = [$ssnInfoType];

    // Create the masking configuration object
    $maskConfig = (new CharacterMaskConfig())
        ->setMaskingCharacter($maskingCharacter)
        ->setNumberToMask($numberToMask);

    // Create the information transform configuration objects
    $primitiveTransformation = (new PrimitiveTransformation())
        ->setCharacterMaskConfig($maskConfig);

    $infoTypeTransformation = (new InfoTypeTransformation())
        ->setPrimitiveTransformation($primitiveTransformation)
        ->setInfoTypes($infoTypes);

    $infoTypeTransformations = (new InfoTypeTransformations())
        ->setTransformations([$infoTypeTransformation]);

    // Create the deidentification configuration object
    $deidentifyConfig = (new DeidentifyConfig())
        ->setInfoTypeTransformations($infoTypeTransformations);

    $item = (new ContentItem())
        ->setValue($string);

    $parent = $dlp->projectName($callingProjectId);

    // Run request
    $response = $dlp->deidentifyContent($parent, [
        'deidentifyConfig' => $deidentifyConfig,
        'item' => $item
    ]);

    // Print the results
    $deidentifiedValue = $response->getItem()->getValue();
    print($deidentifiedValue);
}

C#

public static object DeidMask(
    string projectId,
    string dataValue,
    IEnumerable<InfoType> infoTypes,
    string maskingCharacter,
    int numberToMask,
    bool reverseOrder)
{
    var request = new DeidentifyContentRequest
    {
        ParentAsProjectName = new ProjectName(projectId),
        InspectConfig = new InspectConfig
        {
            InfoTypes = { infoTypes }
        },
        DeidentifyConfig = new DeidentifyConfig
        {
            InfoTypeTransformations = new InfoTypeTransformations
            {
                Transformations = {
                    new InfoTypeTransformations.Types.InfoTypeTransformation
                    {
                        PrimitiveTransformation = new PrimitiveTransformation
                        {
                            CharacterMaskConfig = new CharacterMaskConfig
                            {
                                MaskingCharacter = maskingCharacter,
                                NumberToMask = numberToMask,
                                ReverseOrder = reverseOrder
                            }
                        }
                    }
                }
            }
        },
        Item = new ContentItem
        {
            Value = dataValue
        }
    };

    DlpServiceClient dlp = DlpServiceClient.Create();
    var response = dlp.DeidentifyContent(request);

    Console.WriteLine($"Deidentified content: {response.Item.Value}");
    return 0;
}

暗号ベースのトークン化

暗号ベースのトークン化(「仮名化」とも呼ばれる)変換は、元の機密データ値を暗号化された値に置換します。Cloud DLP では、逆行でき「再識別」が可能な変換を含む次のトークン化をサポートしています。

  • 暗号ハッシュ: CryptoKey を基に、入力値で SHA-256 ベースのメッセージ認証コード(HMAC-SHA-256)を使用し、その入力値を Base64 でエンコードされたハッシュ値に置換します。
  • フォーマット保持暗号化: 入力値を、FFX モードのオペレーションでフォーマット保持暗号化(FPE)を使用して生成されたトークンに置換します。この変換方法では、入力値と同じアルファベットで同じ長さに制限されたトークンが生成されます。FPE は、元の暗号鍵に基づく再識別もサポートしています。
  • 確定的暗号化: 入力値を、合成初期化ベクトルモード(AES-SIV)の AES を使用して生成されたトークンに置換します。この変換方法では、サポートされる文字列セットに制限がなく、同じ入力値の各インスタンスに対して同じトークンが生成され、サロゲート値を使用した元の暗号鍵に基づく再識別が可能です。

暗号ハッシュ

暗号ハッシュ トークン化変換(DLP API の CryptoHashConfig)では、入力値(Cloud DLP が検出した機密データの一部)を受け取ってハッシュ値に置換します。ハッシュ値は、CryptoKey を基に SHA-256 ベースのメッセージ認証コード(HMAC-SHA-256)を入力値に使用することにより生成されます。

Cloud DLP は、ハッシュされた入力値の Base64 エンコード表現を元の値の代わりに出力します。

暗号ハッシュ変換を使用する際には、次の点に注意してください。

  • 入力値は暗号化されずにハッシュされます。
  • この操作は元に戻せません。つまり、ハッシュされた変換の出力値と元の暗号鍵から元の値を復元する方法はありません。
  • 現時点では、ハッシュできるのは文字列と整数値のみです。
  • 暗号鍵のサイズに応じて、変換のハッシュ出力は常に同じ長さになります。たとえば、10 桁の電話番号に暗号ハッシュ変換を使用すると、各電話番号は固定長の Base64 エンコードのハッシュ値に置き換えられます。

フォーマット保持暗号化

フォーマット保持暗号化変換方式(DLP API の CryptoReplaceFfxFpeConfig)では、入力値(Cloud DLP が検出した機密データの一部)を受け取り、それを FFX モードのフォーマット保持暗号化と CryptoKey を使用して元の値を暗号化された値(トークン)に置き換えます。

入力値は次に従うものとします。

  • 2 文字以上にするか、空の文字列にする必要があります。
  • ASCII としてエンコードする必要があります。
  • 「アルファベット」で指定された文字で構成されます。これは、入力値に許容される 2~64 の一連の文字です。詳細については、CryptoReplaceFfxFpeConfig の alphabet フィールドをご覧ください。

生成されるトークンは次のとおりです。

  • 暗号化された入力値です。
  • 暗号化後の文字セット(「アルファベット」)と入力値の長さを保持します。
  • 指定された暗号鍵をキーとした FFX モードのフォーマット保持暗号化(FPE)を使用して計算されます。
  • 同じ入力値の各インスタンスが同じトークンに匿名化されるため、必ずしも一意ではありません。これにより参照整合性が有効になり、匿名化されたデータでより効率的な検索が可能になります。コンテキストで説明されているとおり、コンテキストの「微調整」によりこの動作を変更できます。

ソース コンテンツに入力値のインスタンスが複数ある場合は、それぞれが同じトークンに対して匿名化されます。FPE は、長さとアルファベット空間(文字セット)の両方を保持します。文字数は、62 文字に制限されています。この動作は、同じ入力値の各インスタンスを一意のトークンに匿名化する非確定的トークン化手法とは異なり、任意の文字セットで使用できます。セキュリティの向上ができるコンテキスト「微調整」によりこの動作を変更できます。変換にコンテキスト微調整を追加することで、異なるトークンに対する同じ入力値の複数のインスタンスの匿名化が可能です。元の値の長さとアルファベット空間を保持する必要がない場合は、後述の確定的暗号化を使用してください。

Cloud DLP では、暗号鍵を使用して置換トークンが計算されます。この鍵は、次の 3 つの方法のいずれかで指定します。

  1. 暗号化せずに API リクエストに埋め込む。
  2. Cloud DLP で生成されるようにリクエストする。
  3. 暗号化して API リクエストに埋め込む。このオプションでは、鍵は Cloud Key Management Service(Cloud KMS)の鍵によってラップ(暗号化)されます。

Cloud KMS でラップされた鍵を作成するには、16、24、32 バイトの平文フィールド値を含むリクエストを Cloud KMS の projects.locations.keyRings.cryptoKeys.encrypt メソッドに送信します。ラップ鍵は、メソッドのレスポンスの ciphertext フィールドの値です。

この値は、デフォルトでは Base64 でエンコードされた文字列です。この値を Cloud DLP で設定するには、バイト文字列に復号する必要があります。次のコード スニペットは、この方法をいくつかの言語で示しています。これらのスニペットに続いて、エンドツーエンドの例が示されています。

Java

KmsWrappedCryptoKey.newBuilder()
    .setWrappedKey(ByteString.copyFrom(BaseEncoding.base64().decode(wrappedKey)))

Python

# The wrapped key is Base64-encoded, but the library expects a binary
# string, so decode it here.
import base64
wrapped_key = base64.b64decode(wrapped_key)

PHP

// Create the wrapped crypto key configuration object
$kmsWrappedCryptoKey = (new KmsWrappedCryptoKey())
    ->setWrappedKey(base64_decode($wrappedKey))
    ->setCryptoKeyName($keyName);

C#

WrappedKey = ByteString.FromBase64(wrappedKey)

Cloud KMS を使用したデータの暗号化と復号の詳細については、データの暗号化と復号をご覧ください。

以下に、Cloud DLP を使用して機密データの匿名化(トークンによる入力値の置換)を行うサンプルコードをいくつかの言語で示します。

Java

/**
 * Deidentify a string by encrypting sensitive information while preserving format.
 *
 * @param string The string to deidentify.
 * @param alphabet The set of characters to use when encrypting the input. For more information,
 *     see cloud.google.com/dlp/docs/reference/rest/v2/content/deidentify
 * @param keyName The name of the Cloud KMS key to use when decrypting the wrapped key.
 * @param wrappedKey The encrypted (or "wrapped") AES-256 encryption key.
 * @param projectId ID of Google Cloud project to run the API under.
 */
private static void deIdentifyWithFpe(
    String string,
    List<InfoType> infoTypes,
    FfxCommonNativeAlphabet alphabet,
    String keyName,
    String wrappedKey,
    String projectId,
    String surrogateType) {
  // instantiate a client
  try (DlpServiceClient dlpServiceClient = DlpServiceClient.create()) {
    ContentItem contentItem = ContentItem.newBuilder().setValue(string).build();

    // Create the format-preserving encryption (FPE) configuration
    KmsWrappedCryptoKey kmsWrappedCryptoKey =
        KmsWrappedCryptoKey.newBuilder()
            .setWrappedKey(ByteString.copyFrom(BaseEncoding.base64().decode(wrappedKey)))
            .setCryptoKeyName(keyName)
            .build();

    CryptoKey cryptoKey = CryptoKey.newBuilder().setKmsWrapped(kmsWrappedCryptoKey).build();

    CryptoReplaceFfxFpeConfig cryptoReplaceFfxFpeConfig =
        CryptoReplaceFfxFpeConfig.newBuilder()
            .setCryptoKey(cryptoKey)
            .setCommonAlphabet(alphabet)
            .setSurrogateInfoType(InfoType.newBuilder().setName(surrogateType).build())
            .build();

    // Create the deidentification transformation configuration
    PrimitiveTransformation primitiveTransformation =
        PrimitiveTransformation.newBuilder()
            .setCryptoReplaceFfxFpeConfig(cryptoReplaceFfxFpeConfig)
            .build();

    InfoTypeTransformation infoTypeTransformationObject =
        InfoTypeTransformation.newBuilder()
            .setPrimitiveTransformation(primitiveTransformation)
            .build();

    InfoTypeTransformations infoTypeTransformationArray =
        InfoTypeTransformations.newBuilder()
            .addTransformations(infoTypeTransformationObject)
            .build();

    InspectConfig inspectConfig =
        InspectConfig.newBuilder()
            .addAllInfoTypes(infoTypes)
            .build();

    // Create the deidentification request object
    DeidentifyConfig deidentifyConfig =
        DeidentifyConfig.newBuilder()
            .setInfoTypeTransformations(infoTypeTransformationArray)
            .build();

    DeidentifyContentRequest request =
        DeidentifyContentRequest.newBuilder()
            .setParent(ProjectName.of(projectId).toString())
            .setInspectConfig(inspectConfig)
            .setDeidentifyConfig(deidentifyConfig)
            .setItem(contentItem)
            .build();

    // Execute the deidentification request
    DeidentifyContentResponse response = dlpServiceClient.deidentifyContent(request);

    // Print the deidentified input value
    // e.g. "My SSN is 123456789" --> "My SSN is 7261298621"
    String result = response.getItem().getValue();
    System.out.println(result);
  } catch (Exception e) {
    System.out.println("Error in deidentifyWithFpe: " + e.getMessage());
  }
}

Node.js

// Imports the Google Cloud Data Loss Prevention library
const DLP = require('@google-cloud/dlp');

// Instantiates a client
const dlp = new DLP.DlpServiceClient();

// The project ID to run the API call under
// const callingProjectId = process.env.GCLOUD_PROJECT;

// The string to deidentify
// const string = 'My SSN is 372819127';

// The set of characters to replace sensitive ones with
// For more information, see https://cloud.google.com/dlp/docs/reference/rest/v2/organizations.deidentifyTemplates#ffxcommonnativealphabet
// const alphabet = 'ALPHA_NUMERIC';

// The name of the Cloud KMS key used to encrypt ('wrap') the AES-256 key
// const keyName = 'projects/YOUR_GCLOUD_PROJECT/locations/YOUR_LOCATION/keyRings/YOUR_KEYRING_NAME/cryptoKeys/YOUR_KEY_NAME';

// The encrypted ('wrapped') AES-256 key to use
// This key should be encrypted using the Cloud KMS key specified above
// const wrappedKey = 'YOUR_ENCRYPTED_AES_256_KEY'

// (Optional) The name of the surrogate custom info type to use
// Only necessary if you want to reverse the deidentification process
// Can be essentially any arbitrary string, as long as it doesn't appear
// in your dataset otherwise.
// const surrogateType = 'SOME_INFO_TYPE_DEID';

// Construct FPE config
const cryptoReplaceFfxFpeConfig = {
  cryptoKey: {
    kmsWrapped: {
      wrappedKey: wrappedKey,
      cryptoKeyName: keyName,
    },
  },
  commonAlphabet: alphabet,
};
if (surrogateType) {
  cryptoReplaceFfxFpeConfig.surrogateInfoType = {
    name: surrogateType,
  };
}

// Construct deidentification request
const item = {value: string};
const request = {
  parent: dlp.projectPath(callingProjectId),
  deidentifyConfig: {
    infoTypeTransformations: {
      transformations: [
        {
          primitiveTransformation: {
            cryptoReplaceFfxFpeConfig: cryptoReplaceFfxFpeConfig,
          },
        },
      ],
    },
  },
  item: item,
};

try {
  // Run deidentification request
  const [response] = await dlp.deidentifyContent(request);
  const deidentifiedItem = response.item;
  console.log(deidentifiedItem.value);
} catch (err) {
  console.log(`Error in deidentifyWithFpe: ${err.message || err}`);
}

Python

def deidentify_with_fpe(project, string, info_types, alphabet=None,
                        surrogate_type=None, key_name=None, wrapped_key=None):
    """Uses the Data Loss Prevention API to deidentify sensitive data in a
    string using Format Preserving Encryption (FPE).
    Args:
        project: The Google Cloud project id to use as a parent resource.
        item: The string to deidentify (will be treated as text).
        alphabet: The set of characters to replace sensitive ones with. For
            more information, see https://cloud.google.com/dlp/docs/reference/
            rest/v2beta2/organizations.deidentifyTemplates#ffxcommonnativealphabet
        surrogate_type: The name of the surrogate custom info type to use. Only
            necessary if you want to reverse the deidentification process. Can
            be essentially any arbitrary string, as long as it doesn't appear
            in your dataset otherwise.
        key_name: The name of the Cloud KMS key used to encrypt ('wrap') the
            AES-256 key. Example:
            key_name = 'projects/YOUR_GCLOUD_PROJECT/locations/YOUR_LOCATION/
            keyRings/YOUR_KEYRING_NAME/cryptoKeys/YOUR_KEY_NAME'
        wrapped_key: The encrypted ('wrapped') AES-256 key to use. This key
            should be encrypted using the Cloud KMS key specified by key_name.
    Returns:
        None; the response from the API is printed to the terminal.
    """
    # Import the client library
    import google.cloud.dlp

    # Instantiate a client
    dlp = google.cloud.dlp.DlpServiceClient()

    # Convert the project id into a full resource id.
    parent = dlp.project_path(project)

    # The wrapped key is base64-encoded, but the library expects a binary
    # string, so decode it here.
    import base64
    wrapped_key = base64.b64decode(wrapped_key)

    # Construct FPE configuration dictionary
    crypto_replace_ffx_fpe_config = {
        'crypto_key': {
            'kms_wrapped': {
                'wrapped_key': wrapped_key,
                'crypto_key_name': key_name
            }
        },
        'common_alphabet': alphabet
    }

    # Add surrogate type
    if surrogate_type:
        crypto_replace_ffx_fpe_config['surrogate_info_type'] = {
            'name': surrogate_type
        }

    # Construct inspect configuration dictionary
    inspect_config = {
        'info_types': [{'name': info_type} for info_type in info_types]
    }

    # Construct deidentify configuration dictionary
    deidentify_config = {
        'info_type_transformations': {
            'transformations': [
                {
                    'primitive_transformation': {
                        'crypto_replace_ffx_fpe_config':
                            crypto_replace_ffx_fpe_config
                    }
                }
            ]
        }
    }

    # Convert string to item
    item = {'value': string}

    # Call the API
    response = dlp.deidentify_content(
        parent, inspect_config=inspect_config,
        deidentify_config=deidentify_config, item=item)

    # Print results
    print(response.item.value)

Go

// deidentifyFPE deidentifies the input with FPE (Format Preserving Encryption).
// keyFileName is the file name with the KMS wrapped key and cryptoKeyName is the
// full KMS key resource name used to wrap the key. surrogateInfoType is an
// optional identifier needed for reidentification. surrogateInfoType can be any
// value not found in your input.
func deidentifyFPE(w io.Writer, client *dlp.Client, project, input string, infoTypes []string, keyFileName, cryptoKeyName, surrogateInfoType string) {
	// Convert the info type strings to a list of InfoTypes.
	var i []*dlppb.InfoType
	for _, it := range infoTypes {
		i = append(i, &dlppb.InfoType{Name: it})
	}
	// Read the key file.
	keyBytes, err := ioutil.ReadFile(keyFileName)
	if err != nil {
		log.Fatalf("error reading file: %v", err)
	}
	// Create a configured request.
	req := &dlppb.DeidentifyContentRequest{
		Parent: "projects/" + project,
		InspectConfig: &dlppb.InspectConfig{
			InfoTypes: i,
		},
		DeidentifyConfig: &dlppb.DeidentifyConfig{
			Transformation: &dlppb.DeidentifyConfig_InfoTypeTransformations{
				InfoTypeTransformations: &dlppb.InfoTypeTransformations{
					Transformations: []*dlppb.InfoTypeTransformations_InfoTypeTransformation{
						{
							InfoTypes: []*dlppb.InfoType{}, // Match all info types.
							PrimitiveTransformation: &dlppb.PrimitiveTransformation{
								Transformation: &dlppb.PrimitiveTransformation_CryptoReplaceFfxFpeConfig{
									CryptoReplaceFfxFpeConfig: &dlppb.CryptoReplaceFfxFpeConfig{
										CryptoKey: &dlppb.CryptoKey{
											Source: &dlppb.CryptoKey_KmsWrapped{
												KmsWrapped: &dlppb.KmsWrappedCryptoKey{
													WrappedKey:    keyBytes,
													CryptoKeyName: cryptoKeyName,
												},
											},
										},
										// Set the alphabet used for the output.
										Alphabet: &dlppb.CryptoReplaceFfxFpeConfig_CommonAlphabet{
											CommonAlphabet: dlppb.CryptoReplaceFfxFpeConfig_ALPHA_NUMERIC,
										},
										// Set the surrogate info type, used for reidentification.
										SurrogateInfoType: &dlppb.InfoType{
											Name: surrogateInfoType,
										},
									},
								},
							},
						},
					},
				},
			},
		},
		// The item to analyze.
		Item: &dlppb.ContentItem{
			DataItem: &dlppb.ContentItem_Value{
				Value: input,
			},
		},
	}
	// Send the request.
	r, err := client.DeidentifyContent(context.Background(), req)
	if err != nil {
		log.Fatal(err)
	}
	// Print the result.
	fmt.Fprint(w, r.GetItem().GetValue())
}

PHP

use Google\Cloud\Dlp\V2\CryptoReplaceFfxFpeConfig;
use Google\Cloud\Dlp\V2\CryptoReplaceFfxFpeConfig\FfxCommonNativeAlphabet;
use Google\Cloud\Dlp\V2\CryptoKey;
use Google\Cloud\Dlp\V2\DlpServiceClient;
use Google\Cloud\Dlp\V2\PrimitiveTransformation;
use Google\Cloud\Dlp\V2\KmsWrappedCryptoKey;
use Google\Cloud\Dlp\V2\InfoType;
use Google\Cloud\Dlp\V2\DeidentifyConfig;
use Google\Cloud\Dlp\V2\InfoTypeTransformations\InfoTypeTransformation;
use Google\Cloud\Dlp\V2\InfoTypeTransformations;
use Google\Cloud\Dlp\V2\ContentItem;

/**
 * Deidentify a string using Format-Preserving Encryption (FPE).
 *
 * @param string $callingProjectId The GCP Project ID to run the API call under
 * @param string $string The string to deidentify
 * @param string $keyName The name of the Cloud KMS key used to encrypt ('wrap') the AES-256 key
 * @param wrappedKey $wrappedKey The AES-256 key to use, encrypted ('wrapped') with the KMS key
 *        defined by $keyName.
 * @param string $surrogateTypeName Optional surrogate custom info type to enable
 *        reidentification. Can be essentially any arbitrary string that doesn't
 *        appear in your dataset'
 */
function deidentify_fpe(
    $callingProjectId,
    $string,
    $keyName,
    $wrappedKey,
    $surrogateTypeName = ''
) {
    // Instantiate a client.
    $dlp = new DlpServiceClient();

    // The infoTypes of information to mask
    $ssnInfoType = (new InfoType())
        ->setName('US_SOCIAL_SECURITY_NUMBER');
    $infoTypes = [$ssnInfoType];

    // Create the wrapped crypto key configuration object
    $kmsWrappedCryptoKey = (new KmsWrappedCryptoKey())
        ->setWrappedKey(base64_decode($wrappedKey))
        ->setCryptoKeyName($keyName);

    // The set of characters to replace sensitive ones with
    // For more information, see https://cloud.google.com/dlp/docs/reference/rest/V2/organizations.deidentifyTemplates#ffxcommonnativealphabet
    $commonAlphabet = FfxCommonNativeAlphabet::NUMERIC;

    // Create the crypto key configuration object
    $cryptoKey = (new CryptoKey())
        ->setKmsWrapped($kmsWrappedCryptoKey);

    // Create the crypto FFX FPE configuration object
    $cryptoReplaceFfxFpeConfig = (new CryptoReplaceFfxFpeConfig())
        ->setCryptoKey($cryptoKey)
        ->setCommonAlphabet($commonAlphabet);
    if ($surrogateTypeName) {
        $surrogateType = (new InfoType())
            ->setName($surrogateTypeName);
        $cryptoReplaceFfxFpeConfig->setSurrogateInfoType($surrogateType);
    }

    // Create the information transform configuration objects
    $primitiveTransformation = (new PrimitiveTransformation())
        ->setCryptoReplaceFfxFpeConfig($cryptoReplaceFfxFpeConfig);

    $infoTypeTransformation = (new InfoTypeTransformation())
        ->setPrimitiveTransformation($primitiveTransformation)
        ->setInfoTypes($infoTypes);

    $infoTypeTransformations = (new InfoTypeTransformations())
        ->setTransformations([$infoTypeTransformation]);

    // Create the deidentification configuration object
    $deidentifyConfig = (new DeidentifyConfig())
        ->setInfoTypeTransformations($infoTypeTransformations);

    $content = (new ContentItem())
        ->setValue($string);

    $parent = $dlp->projectName($callingProjectId);

    // Run request
    $response = $dlp->deidentifyContent($parent, [
        'deidentifyConfig' => $deidentifyConfig,
        'item' => $content
    ]);

    // Print the results
    $deidentifiedValue = $response->getItem()->getValue();
    print($deidentifiedValue);
}

C#

public static object DeidFpe(
    string projectId,
    string dataValue,
    IEnumerable<InfoType> infoTypes,
    string keyName,
    string wrappedKey,
    string alphabet)
{
    var deidentifyConfig = new DeidentifyConfig
    {
        InfoTypeTransformations = new InfoTypeTransformations
        {
            Transformations =
            {
                new InfoTypeTransformations.Types.InfoTypeTransformation
                {
                    PrimitiveTransformation = new PrimitiveTransformation
                    {
                        CryptoReplaceFfxFpeConfig = new CryptoReplaceFfxFpeConfig
                        {
                            CommonAlphabet = (FfxCommonNativeAlphabet) Enum.Parse(typeof(FfxCommonNativeAlphabet), alphabet),
                            CryptoKey = new CryptoKey
                            {
                                KmsWrapped = new KmsWrappedCryptoKey
                                {
                                    CryptoKeyName = keyName,
                                    WrappedKey = ByteString.FromBase64(wrappedKey)
                                }
                            },
                            SurrogateInfoType = new InfoType
                            {
                                Name = "TOKEN"
                            }
                        }
                    }
                }
            }
        }
    };

    DlpServiceClient dlp = DlpServiceClient.Create();
    var response = dlp.DeidentifyContent(
        new DeidentifyContentRequest
        {
            ParentAsProjectName = new ProjectName(projectId),
            InspectConfig = new InspectConfig
            {
                InfoTypes = { infoTypes }
            },
            DeidentifyConfig = deidentifyConfig,
            Item = new ContentItem { Value = dataValue }
        });

    Console.WriteLine($"Deidentified content: {response.Item.Value}");
    return 0;
}

確定的暗号化

DLP API の確定的暗号化変換方式 CryptoDeterministicConfig では、入力値(Cloud DLP が検出した機密データ)を AES-SIVCryptoKey で暗号化してから、元の値を Base64 エンコードの暗号化された値に置換します。

確定的暗号化変換を使用すると、暗号化データをより効率的に検索できます。

入力値は次に従うものとします。

  • 1 文字以上でなければなりません。
  • 文字セットの制限はありません。

生成されるトークンは次のとおりです。

  • 暗号化された値の Base64 エンコード表現です。
  • 暗号化後の文字セット(「アルファベット」)と入力値の長さを保持しません。
  • CryptoKey に基づき SIV モードでの AES 暗号化(AES-SIV)を使用して計算されます。
  • 同じ入力値の各インスタンスが同じトークンに匿名化されるため、必ずしも一意ではありません。これにより、暗号化データをより効率的に検索できます。コンテキストで説明されているとおり、コンテキストの「微調整」によりこの動作を変更できます。
  • [SURROGATE_TYPE]([LENGTH]): という形式で接頭辞を追加して生成されます。ここで、[SURROGATE_TYPE] は入力値のサロゲート値の infoType を表し、[LENGTH] は文字の長さを表します。サロゲート値により、匿名化に使用した元の暗号鍵に基づくトークンの再識別が可能になります。

以下は、確定的暗号化を使用して匿名化する JSON 構成の例です。電話番号の匿名化であるため、記述的なサロゲート タイプとして "PHONE_SURROGATE" が使用されています。 ここで、[CRYPTO_KEY] は Cloud KMS から取得したラップされていない暗号鍵を表します。CryptoKey を取得する方法の詳細については、前のセクションのフォーマット保持暗号化をご覧ください。

{
  "deidentifyConfig":{
    "infoTypeTransformations":{
      "transformations":[
        {
          "infoTypes":[
            {
              "name":"PHONE_NUMBER"
            }
          ],
          "primitiveTransformation":{
            "cryptoDeterministicConfig":{
              "cryptoKey":{
                "unwrapped":{
                  "key":"[CRYPTO_KEY]"
                }
              },
              "surrogateInfoType":{
                "name":"PHONE_SURROGATE"
              }
            }
          }
        }
      ]
    }
  },
  "inspectConfig":{
    "infoTypes":[
      {
        "name":"PHONE_NUMBER"
      }
    ]
  },
  "item":{
    "value":"My phone number is 206-555-0574, call me"
  }
}

この変換を使用して文字列 "My phone number is 206-555-0574" を匿名化すると、次のような匿名化された文字列が生成されます。

My phone number is PHONE_SURROGATE(36):ATZBu5OCCSwo+e94xSYnKYljk1OQpkW7qhzx, call me

この文字列を再識別するには、次のような JSON リクエストを使用します。ここで、[CRYPTO_KEY] は、コンテンツの匿名化に使用されたものと同じ暗号鍵です。

{
  "reidentifyConfig":{
    "infoTypeTransformations":{
      "transformations":[
        {
          "infoTypes":[
            {
              "name":"PHONE_SURROGATE"
            }
          ],
          "primitiveTransformation":{
            "cryptoDeterministicConfig":{
              "cryptoKey":{
                "unwrapped":{
                  "key":"[CRYPTO_KEY]"
                }
              },
              "surrogateInfoType":{
                "name":"PHONE_SURROGATE"
              }
            }
          }
        }
      ]
    }
  },
  "inspectConfig":{
    "customInfoTypes":[
      {
        "infoType":{
          "name":"PHONE_SURROGATE"
        },
        "surrogateType":{

        }
      }
    ]
  },
  "item":{
    "value":"My phone number is [PHONE_SURROGATE](36):ATZBu5OCCSwo+e94xSYnKYljk1OQpkW7qhzx, call me"
  }
}

この文字列を再識別すると、元の文字列になります。

My phone number is 206-555-0574, call me

バケット

バケット変換は、数値データを範囲ごとに「バケット化」することによる数値データの匿名化です。出力される数値範囲は、下限、ハイフン、上限から構成されるハイフン付きの文字列で表現されます。

固定サイズのバケット化

Cloud DLP では、固定サイズの範囲(DLP API の FixedSizeBucketingConfig)に基づいて入力数値をバケット化できます。固定サイズのバケットを構成するには、次のように指定します。

  • すべてのバケットの下限値。これより小さい値は 1 つのバケットにまとめられます。
  • すべてのバケットの上限値。これより大きい値は 1 つのバケットにまとめられます。
  • 最小バケットと最大バケット以外の、各バケットのサイズ。

たとえば、下限を 10、上限を 89、バケットサイズを 10 に設定した場合、-10、10-20、20-30、30-40、40-50、50-60、60-70、70-80、80-89、89+ のバケットが設定されます。

バケット化の詳細については、一般化とバケット化をご覧ください。

カスタマイズ可能なバケット化

カスタマイズ可能なバケット化(DLP API の BucketingConfig)は、固定サイズのバケット化よりも柔軟性があります。上限、下限、間隔値(均等サイズのバケットを作成するためのもの)ではなく、作成するバケットの最大値と最小値を指定します。最大値と最小値のペアは同じ型でなければなりません。

カスタマイズ可能なバケット化の設定には、個別のバケットを指定します。各バケットのプロパティは次のとおりです。

  • バケットの範囲の下限。下限のないバケットを作成する場合、この値は省略します。
  • バケットの範囲の上限。上限のないバケットを作成する場合、この値は省略します。
  • バケット範囲の置換値。これは下限と上限の範囲内にあるすべての検出値を置き換えるための値です。置換値を指定しない場合は、代わりに、ハイフン付きの最小値と最大値の範囲が生成されます。

たとえば、次のバケット化変換の JSON 構成を考えます。

"bucketingConfig":{
  "buckets":[
    {
      "min":{
        "integerValue":"1"
      },
      "max":{
        "integerValue":"30"
      },
      "replacementValue":{
        "stringValue":"LOW"
      }
    },
    {
      "min":{
        "integerValue":"31"
      },
      "max":{
        "integerValue":"65"
      },
      "replacementValue":{
        "stringValue":"MEDIUM"
      }
    },
    {
      "min":{
        "integerValue":"66"
      },
      "max":{
        "integerValue":"100"
      },
      "replacementValue":{
        "stringValue":"HIGH"
      }
    }
  ]
}

これにより、次のような動作が定義されます。

  • 1〜30 の整数値は、LOW で置換することでマスキングされます。
  • 31〜65 の整数値は、MEDIUM で置換することでマスキングされます。
  • 66~100 の整数値は、HIGH で置換することでマスキングされます。

バケット化の概念の詳細については、一般化とバケット化をご覧ください。

日付シフト

日付入力値に対して日付シフト変換(DLP API の DateShiftConfig)を使用すると、日付がランダムな日数だけシフトされます。

日付シフト手法では、日付のセットがランダムにシフトされますが、期間の順序と持続時間は保持されます。通常、日付シフトは、個人またはエンティティのコンテキストで使用されます。特定の個人の日付はすべて同じシフト差異でシフトされますが、別の個人には異なるシフト差異が使用されます。

日付シフトの詳細については、日付シフトをご覧ください。

以下に、Cloud DLP API を使用して日付の匿名化(日付シフト)を行うサンプルコードをいくつかの言語で示します。

Java

/**
 * @param inputCsvPath The path to the CSV file to deidentify
 * @param outputCsvPath (Optional) path to the output CSV file
 * @param dateFields The list of (date) fields in the CSV file to date shift
 * @param lowerBoundDays The maximum number of days to shift a date backward
 * @param upperBoundDays The maximum number of days to shift a date forward
 * @param contextFieldId (Optional) The column to determine date shift, default : a random shift
 *     amount
 * @param wrappedKey (Optional) The encrypted ('wrapped') AES-256 key to use when shifting dates
 * @param keyName (Optional) The name of the Cloud KMS key used to encrypt ('wrap') the AES-256
 *     key
 * @param projectId ID of Google Cloud project to run the API under.
 */
private static void deidentifyWithDateShift(
    Path inputCsvPath,
    Path outputCsvPath,
    String[] dateFields,
    int lowerBoundDays,
    int upperBoundDays,
    String contextFieldId,
    String wrappedKey,
    String keyName,
    String projectId)
    throws Exception {
  // instantiate a client
  try (DlpServiceClient dlpServiceClient = DlpServiceClient.create()) {

    // Set the maximum days to shift a day backward (lowerbound), forward (upperbound)
    DateShiftConfig.Builder dateShiftConfigBuilder =
        DateShiftConfig.newBuilder()
            .setLowerBoundDays(lowerBoundDays)
            .setUpperBoundDays(upperBoundDays);

    // If contextFieldId, keyName or wrappedKey is set: all three arguments must be valid
    if (contextFieldId != null && keyName != null && wrappedKey != null) {
      dateShiftConfigBuilder.setContext(FieldId.newBuilder().setName(contextFieldId).build());
      KmsWrappedCryptoKey kmsWrappedCryptoKey =
          KmsWrappedCryptoKey.newBuilder()
              .setCryptoKeyName(keyName)
              .setWrappedKey(ByteString.copyFrom(BaseEncoding.base64().decode(wrappedKey)))
              .build();
      dateShiftConfigBuilder.setCryptoKey(
          CryptoKey.newBuilder().setKmsWrapped(kmsWrappedCryptoKey).build());

    } else if (contextFieldId != null || keyName != null || wrappedKey != null) {
      throw new IllegalArgumentException(
          "You must set either ALL or NONE of {contextFieldId, keyName, wrappedKey}!");
    }

    // Read and parse the CSV file
    BufferedReader br = null;
    String line;
    List<Table.Row> rows = new ArrayList<>();
    List<FieldId> headers;

    br = new BufferedReader(new FileReader(inputCsvPath.toFile()));

    // convert csv header to FieldId
    headers =
        Arrays.stream(br.readLine().split(","))
            .map(header -> FieldId.newBuilder().setName(header).build())
            .collect(Collectors.toList());

    while ((line = br.readLine()) != null) {
      // convert csv rows to Table.Row
      rows.add(convertCsvRowToTableRow(line));
    }
    br.close();

    Table table = Table.newBuilder().addAllHeaders(headers).addAllRows(rows).build();

    List<FieldId> dateFieldIds =
        Arrays.stream(dateFields)
            .map(field -> FieldId.newBuilder().setName(field).build())
            .collect(Collectors.toList());

    DateShiftConfig dateShiftConfig = dateShiftConfigBuilder.build();

    FieldTransformation fieldTransformation =
        FieldTransformation.newBuilder()
            .addAllFields(dateFieldIds)
            .setPrimitiveTransformation(
                PrimitiveTransformation.newBuilder().setDateShiftConfig(dateShiftConfig).build())
            .build();

    DeidentifyConfig deidentifyConfig =
        DeidentifyConfig.newBuilder()
            .setRecordTransformations(
                RecordTransformations.newBuilder()
                    .addFieldTransformations(fieldTransformation)
                    .build())
            .build();

    ContentItem tableItem = ContentItem.newBuilder().setTable(table).build();

    DeidentifyContentRequest request =
        DeidentifyContentRequest.newBuilder()
            .setParent(ProjectName.of(projectId).toString())
            .setDeidentifyConfig(deidentifyConfig)
            .setItem(tableItem)
            .build();

    // Execute the deidentification request
    DeidentifyContentResponse response = dlpServiceClient.deidentifyContent(request);

    // Write out the response as a CSV file
    List<FieldId> outputHeaderFields = response.getItem().getTable().getHeadersList();
    List<Table.Row> outputRows = response.getItem().getTable().getRowsList();

    List<String> outputHeaders =
        outputHeaderFields.stream().map(FieldId::getName).collect(Collectors.toList());

    File outputFile = outputCsvPath.toFile();
    if (!outputFile.exists()) {
      outputFile.createNewFile();
    }
    BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(outputFile));

    // write out headers
    bufferedWriter.append(String.join(",", outputHeaders) + "\n");

    // write out each row
    for (Table.Row outputRow : outputRows) {
      String row =
          outputRow
              .getValuesList()
              .stream()
              .map(value -> value.getStringValue())
              .collect(Collectors.joining(","));
      bufferedWriter.append(row + "\n");
    }

    bufferedWriter.flush();
    bufferedWriter.close();

    System.out.println("Successfully saved date-shift output to: " + outputCsvPath.getFileName());
  } catch (Exception e) {
    System.out.println("Error in deidentifyWithDateShift: " + e.getMessage());
  }
}

// Parse string to valid date, return null when invalid
private static LocalDate getValidDate(String dateString) {
  try {
    return LocalDate.parse(dateString);
  } catch (DateTimeParseException e) {
    return null;
  }
}

// convert CSV row into Table.Row
private static Table.Row convertCsvRowToTableRow(String row) {
  String[] values = row.split(",");
  Table.Row.Builder tableRowBuilder = Table.Row.newBuilder();
  for (String value : values) {
    LocalDate date = getValidDate(value);
    if (date != null) {
      // convert to com.google.type.Date
      Date dateValue =
          Date.newBuilder()
              .setYear(date.getYear())
              .setMonth(date.getMonthValue())
              .setDay(date.getDayOfMonth())
              .build();
      Value tableValue = Value.newBuilder().setDateValue(dateValue).build();
      tableRowBuilder.addValues(tableValue);
    } else {
      tableRowBuilder.addValues(Value.newBuilder().setStringValue(value).build());
    }
  }
  return tableRowBuilder.build();
}

Node.js

// Imports the Google Cloud Data Loss Prevention library
const DLP = require('@google-cloud/dlp');

// Instantiates a client
const dlp = new DLP.DlpServiceClient();

// Import other required libraries
const fs = require('fs');

// The project ID to run the API call under
// const callingProjectId = process.env.GCLOUD_PROJECT;

// The path to the CSV file to deidentify
// The first row of the file must specify column names, and all other rows
// must contain valid values
// const inputCsvFile = '/path/to/input/file.csv';

// The path to save the date-shifted CSV file to
// const outputCsvFile = '/path/to/output/file.csv';

// The list of (date) fields in the CSV file to date shift
// const dateFields = [{ name: 'birth_date'}, { name: 'register_date' }];

// The maximum number of days to shift a date backward
// const lowerBoundDays = 1;

// The maximum number of days to shift a date forward
// const upperBoundDays = 1;

// (Optional) The column to determine date shift amount based on
// If this is not specified, a random shift amount will be used for every row
// If this is specified, then 'wrappedKey' and 'keyName' must also be set
// const contextFieldId = [{ name: 'user_id' }];

// (Optional) The name of the Cloud KMS key used to encrypt ('wrap') the AES-256 key
// If this is specified, then 'wrappedKey' and 'contextFieldId' must also be set
// const keyName = 'projects/YOUR_GCLOUD_PROJECT/locations/YOUR_LOCATION/keyRings/YOUR_KEYRING_NAME/cryptoKeys/YOUR_KEY_NAME';

// (Optional) The encrypted ('wrapped') AES-256 key to use when shifting dates
// This key should be encrypted using the Cloud KMS key specified above
// If this is specified, then 'keyName' and 'contextFieldId' must also be set
// const wrappedKey = 'YOUR_ENCRYPTED_AES_256_KEY'

// Helper function for converting CSV rows to Protobuf types
const rowToProto = row => {
  const values = row.split(',');
  const convertedValues = values.map(value => {
    if (Date.parse(value)) {
      const date = new Date(value);
      return {
        dateValue: {
          year: date.getFullYear(),
          month: date.getMonth() + 1,
          day: date.getDate(),
        },
      };
    } else {
      // Convert all non-date values to strings
      return {stringValue: value.toString()};
    }
  });
  return {values: convertedValues};
};

// Read and parse a CSV file
const csvLines = fs
  .readFileSync(inputCsvFile)
  .toString()
  .split('\n')
  .filter(line => line.includes(','));
const csvHeaders = csvLines[0].split(',');
const csvRows = csvLines.slice(1);

// Construct the table object
const tableItem = {
  table: {
    headers: csvHeaders.map(header => {
      return {name: header};
    }),
    rows: csvRows.map(row => rowToProto(row)),
  },
};

// Construct DateShiftConfig
const dateShiftConfig = {
  lowerBoundDays: lowerBoundDays,
  upperBoundDays: upperBoundDays,
};

if (contextFieldId && keyName && wrappedKey) {
  dateShiftConfig.context = {name: contextFieldId};
  dateShiftConfig.cryptoKey = {
    kmsWrapped: {
      wrappedKey: wrappedKey,
      cryptoKeyName: keyName,
    },
  };
} else if (contextFieldId || keyName || wrappedKey) {
  throw new Error(
    'You must set either ALL or NONE of {contextFieldId, keyName, wrappedKey}!'
  );
}

// Construct deidentification request
const request = {
  parent: dlp.projectPath(callingProjectId),
  deidentifyConfig: {
    recordTransformations: {
      fieldTransformations: [
        {
          fields: dateFields,
          primitiveTransformation: {
            dateShiftConfig: dateShiftConfig,
          },
        },
      ],
    },
  },
  item: tableItem,
};

try {
  // Run deidentification request
  const [response] = await dlp.deidentifyContent(request);
  const tableRows = response.item.table.rows;

  // Write results to a CSV file
  tableRows.forEach((row, rowIndex) => {
    const rowValues = row.values.map(
      value =>
        value.stringValue ||
        `${value.dateValue.month}/${value.dateValue.day}/${
          value.dateValue.year
        }`
    );
    csvLines[rowIndex + 1] = rowValues.join(',');
  });
  csvLines.push('');
  fs.writeFileSync(outputCsvFile, csvLines.join('\n'));

  // Print status
  console.log(`Successfully saved date-shift output to ${outputCsvFile}`);
} catch (err) {
  console.log(`Error in deidentifyWithDateShift: ${err.message || err}`);
}

Python

def deidentify_with_date_shift(project, input_csv_file=None,
                               output_csv_file=None, date_fields=None,
                               lower_bound_days=None, upper_bound_days=None,
                               context_field_id=None, wrapped_key=None,
                               key_name=None):
    """Uses the Data Loss Prevention API to deidentify dates in a CSV file by
        pseudorandomly shifting them.
    Args:
        project: The Google Cloud project id to use as a parent resource.
        input_csv_file: The path to the CSV file to deidentify. The first row
            of the file must specify column names, and all other rows must
            contain valid values.
        output_csv_file: The path to save the date-shifted CSV file.
        date_fields: The list of (date) fields in the CSV file to date shift.
            Example: ['birth_date', 'register_date']
        lower_bound_days: The maximum number of days to shift a date backward
        upper_bound_days: The maximum number of days to shift a date forward
        context_field_id: (Optional) The column to determine date shift amount
            based on. If this is not specified, a random shift amount will be
            used for every row. If this is specified, then 'wrappedKey' and
            'keyName' must also be set. Example:
            contextFieldId = [{ 'name': 'user_id' }]
        key_name: (Optional) The name of the Cloud KMS key used to encrypt
            ('wrap') the AES-256 key. Example:
            key_name = 'projects/YOUR_GCLOUD_PROJECT/locations/YOUR_LOCATION/
            keyRings/YOUR_KEYRING_NAME/cryptoKeys/YOUR_KEY_NAME'
        wrapped_key: (Optional) The encrypted ('wrapped') AES-256 key to use.
            This key should be encrypted using the Cloud KMS key specified by
            key_name.
    Returns:
        None; the response from the API is printed to the terminal.
    """
    # Import the client library
    import google.cloud.dlp

    # Instantiate a client
    dlp = google.cloud.dlp.DlpServiceClient()

    # Convert the project id into a full resource id.
    parent = dlp.project_path(project)

    # Convert date field list to Protobuf type
    def map_fields(field):
        return {'name': field}

    if date_fields:
        date_fields = map(map_fields, date_fields)
    else:
        date_fields = []

    # Read and parse the CSV file
    import csv
    from datetime import datetime
    f = []
    with open(input_csv_file, 'r') as csvfile:
        reader = csv.reader(csvfile)
        for row in reader:
            f.append(row)

    #  Helper function for converting CSV rows to Protobuf types
    def map_headers(header):
        return {'name': header}

    def map_data(value):
        try:
            date = datetime.strptime(value, '%m/%d/%Y')
            return {
                'date_value': {
                    'year': date.year,
                    'month': date.month,
                    'day': date.day
                }
            }
        except ValueError:
            return {'string_value': value}

    def map_rows(row):
        return {'values': map(map_data, row)}

    # Using the helper functions, convert CSV rows to protobuf-compatible
    # dictionaries.
    csv_headers = map(map_headers, f[0])
    csv_rows = map(map_rows, f[1:])

    # Construct the table dict
    table_item = {
        'table': {
            'headers': csv_headers,
            'rows': csv_rows
        }
    }

    # Construct date shift config
    date_shift_config = {
        'lower_bound_days': lower_bound_days,
        'upper_bound_days': upper_bound_days
    }

    # If using a Cloud KMS key, add it to the date_shift_config.
    # The wrapped key is base64-encoded, but the library expects a binary
    # string, so decode it here.
    if context_field_id and key_name and wrapped_key:
        import base64
        date_shift_config['context'] = {'name': context_field_id}
        date_shift_config['crypto_key'] = {
            'kms_wrapped': {
                'wrapped_key': base64.b64decode(wrapped_key),
                'crypto_key_name': key_name
            }
        }
    elif context_field_id or key_name or wrapped_key:
        raise ValueError("""You must set either ALL or NONE of
        [context_field_id, key_name, wrapped_key]!""")

    # Construct Deidentify Config
    deidentify_config = {
        'record_transformations': {
            'field_transformations': [
                {
                    'fields': date_fields,
                    'primitive_transformation': {
                        'date_shift_config': date_shift_config
                    }
                }
            ]
        }
    }

    # Write to CSV helper methods
    def write_header(header):
        return header.name

    def write_data(data):
        return data.string_value or '%s/%s/%s' % (data.date_value.month,
                                                  data.date_value.day,
                                                  data.date_value.year)

    # Call the API
    response = dlp.deidentify_content(
        parent, deidentify_config=deidentify_config, item=table_item)

    # Write results to CSV file
    with open(output_csv_file, 'w') as csvfile:
        write_file = csv.writer(csvfile, delimiter=',')
        write_file.writerow(map(write_header, response.item.table.headers))
        for row in response.item.table.rows:
            write_file.writerow(map(write_data, row.values))
    # Print status
    print('Successfully saved date-shift output to {}'.format(
        output_csv_file))

Go

// deidentifyDateShift shifts dates found in the input between lowerBoundDays and
// upperBoundDays.
func deidentifyDateShift(w io.Writer, client *dlp.Client, project string, lowerBoundDays, upperBoundDays int32, input string) {
	// Create a configured request.
	req := &dlppb.DeidentifyContentRequest{
		Parent: "projects/" + project,
		DeidentifyConfig: &dlppb.DeidentifyConfig{
			Transformation: &dlppb.DeidentifyConfig_InfoTypeTransformations{
				InfoTypeTransformations: &dlppb.InfoTypeTransformations{
					Transformations: []*dlppb.InfoTypeTransformations_InfoTypeTransformation{
						{
							InfoTypes: []*dlppb.InfoType{}, // Match all info types.
							PrimitiveTransformation: &dlppb.PrimitiveTransformation{
								Transformation: &dlppb.PrimitiveTransformation_DateShiftConfig{
									DateShiftConfig: &dlppb.DateShiftConfig{
										LowerBoundDays: lowerBoundDays,
										UpperBoundDays: upperBoundDays,
									},
								},
							},
						},
					},
				},
			},
		},
		// The InspectConfig is used to identify the DATE fields.
		InspectConfig: &dlppb.InspectConfig{
			InfoTypes: []*dlppb.InfoType{
				{
					Name: "DATE",
				},
			},
		},
		// The item to analyze.
		Item: &dlppb.ContentItem{
			DataItem: &dlppb.ContentItem_Value{
				Value: input,
			},
		},
	}
	// Send the request.
	r, err := client.DeidentifyContent(context.Background(), req)
	if err != nil {
		log.Fatal(err)
	}
	// Print the result.
	fmt.Fprint(w, r.GetItem().GetValue())
}

PHP

use Google\Cloud\Dlp\V2\ContentItem;
use Google\Cloud\Dlp\V2\CryptoKey;
use Google\Cloud\Dlp\V2\DateShiftConfig;
use Google\Cloud\Dlp\V2\DeidentifyConfig;
use Google\Cloud\Dlp\V2\DlpServiceClient;
use Google\Cloud\Dlp\V2\FieldId;
use Google\Cloud\Dlp\V2\FieldTransformation;
use Google\Cloud\Dlp\V2\KmsWrappedCryptoKey;
use Google\Cloud\Dlp\V2\PrimitiveTransformation;
use Google\Cloud\Dlp\V2\RecordTransformations;
use Google\Cloud\Dlp\V2\Table;
use Google\Cloud\Dlp\V2\Table\Row;
use Google\Cloud\Dlp\V2\Value;
use Google\Type\Date;
use DateTime;

/**
 * Deidentify dates in a CSV file by pseudorandomly shifting them.
 *
 * @param string $callingProject The GCP Project ID to run the API call under
 * @param string $inputCsvFile The path to the CSV file to deidentify
 * @param string $outputCsvFile The path to save the date-shifted CSV file to
 * @param array $dateFieldNames The list of (date) fields in the CSV file to date shift
 * @param string $lowerBoundDays The maximum number of days to shift a date backward
 * @param string $upperBoundDays The maximum number of days to shift a date forward
 * @param string contextFieldName (Optional) The column to determine date shift amount based on
 *        If this is not specified, a random shift amount will be used for every row.
 *        If this is specified, then 'wrappedKey' and 'keyName' must also be set
 * @param string keyName (Optional) The encrypted ('wrapped') AES-256 key to use when shifting dates
 *        If this is specified, then 'wrappedKey' and 'contextFieldName' must also be set
 * @param string wrappedKey (Optional) The name of the Cloud KMS key used to encrypt ('wrap') the AES-256 key
 *        If this is specified, then 'keyName' and 'contextFieldName' must also be set
 */
function deidentify_dates(
    $callingProjectId,
    $inputCsvFile,
    $outputCsvFile,
    $dateFieldNames,
    $lowerBoundDays,
    $upperBoundDays,
    $contextFieldName = '',
    $keyName = '',
    $wrappedKey = ''
) {
    // Instantiate a client.
    $dlp = new DlpServiceClient();

    // Read a CSV file
    $csvLines = file($inputCsvFile, FILE_IGNORE_NEW_LINES);
    $csvHeaders = explode(',', $csvLines[0]);
    $csvRows = array_slice($csvLines, 1);

    // Convert CSV file into protobuf objects
    $tableHeaders = array_map(function ($csvHeader) {
        return (new FieldId)->setName($csvHeader);
    }, $csvHeaders);

    $tableRows = array_map(function ($csvRow) {
        $rowValues = array_map(function ($csvValue) {
            if ($csvDate = DateTime::createFromFormat('m/d/Y', $csvValue)) {
                $date = (new Date())
                    ->setYear((int) $csvDate->format('Y'))
                    ->setMonth((int) $csvDate->format('m'))
                    ->setDay((int) $csvDate->format('d'));
                return (new Value())
                    ->setDateValue($date);
            } else {
                return (new Value())
                    ->setStringValue($csvValue);
            }
        }, explode(',', $csvRow));

        return (new Row())
            ->setValues($rowValues);
    }, $csvRows);

    // Convert date fields into protobuf objects
    $dateFields = array_map(function ($dateFieldName) {
        return (new FieldId())->setName($dateFieldName);
    }, $dateFieldNames);

    // Construct the table object
    $table = (new Table())
        ->setHeaders($tableHeaders)
        ->setRows($tableRows);

    $item = (new ContentItem())
        ->setTable($table);

    // Construct dateShiftConfig
    $dateShiftConfig = (new DateShiftConfig())
        ->setLowerBoundDays($lowerBoundDays)
        ->setUpperBoundDays($upperBoundDays);

    if ($contextFieldName && $keyName && $wrappedKey) {
        $contextField = (new FieldId())
            ->setName($contextFieldName);

        // Create the wrapped crypto key configuration object
        $kmsWrappedCryptoKey = (new KmsWrappedCryptoKey())
            ->setWrappedKey(base64_decode($wrappedKey))
            ->setCryptoKeyName($keyName);

        $cryptoKey = (new CryptoKey())
            ->setKmsWrapped($kmsWrappedCryptoKey);

        $dateShiftConfig
            ->setContext($contextField)
            ->setCryptoKey($cryptoKey);
    } elseif ($contextFieldName || $keyName || $wrappedKey) {
        throw new Exception('You must set either ALL or NONE of {$contextFieldName, $keyName, $wrappedKey}!');
    }

    // Create the information transform configuration objects
    $primitiveTransformation = (new PrimitiveTransformation())
        ->setDateShiftConfig($dateShiftConfig);

    $fieldTransformation = (new FieldTransformation())
        ->setPrimitiveTransformation($primitiveTransformation)
        ->setFields($dateFields);

    $recordTransformations = (new RecordTransformations())
        ->setFieldTransformations([$fieldTransformation]);

    // Create the deidentification configuration object
    $deidentifyConfig = (new DeidentifyConfig())
        ->setRecordTransformations($recordTransformations);

    $parent = $dlp->projectName($callingProjectId);

    // Run request
    $response = $dlp->deidentifyContent($parent, [
        'deidentifyConfig' => $deidentifyConfig,
        'item' => $item
    ]);

    // Check for errors
    foreach ($response->getOverview()->getTransformationSummaries() as $summary) {
        foreach ($summary->getResults() as $result) {
            if ($details = $result->getDetails()) {
                printf('Error: %s' . PHP_EOL, $details);
                return;
            }
        }
    }

    // Save the results to a file
    $csvRef = fopen($outputCsvFile, 'w');
    fputcsv($csvRef, $csvHeaders);
    foreach ($response->getItem()->getTable()->getRows() as $tableRow) {
        $values = array_map(function ($tableValue) {
            if ($tableValue->getStringValue()) {
                return $tableValue->getStringValue();
            }
            $protoDate = $tableValue->getDateValue();
            $date = mktime(0, 0, 0, $protoDate->getMonth(), $protoDate->getDay(), $protoDate->getYear());
            return strftime('%D', $date);
        }, iterator_to_array($tableRow->getValues()));
        fputcsv($csvRef, $values);
    };
    fclose($csvRef);
    printf('Deidentified dates written to %s' . PHP_EOL, $outputCsvFile);
}

C#

public static object DeidDateShift(
    string projectId,
    string inputCsvFile,
    string outputCsvFile,
    int lowerBoundDays,
    int upperBoundDays,
    string dateFields,
    string contextField = "",
    string keyName = "",
    string wrappedKey = "")
{
    var dlp = DlpServiceClient.Create();

    // Read file
    string[] csvLines = File.ReadAllLines(inputCsvFile);
    string[] csvHeaders = csvLines[0].Split(',');
    string[] csvRows = csvLines.Skip(1).ToArray();

    // Convert dates to protobuf format, and everything else to a string
    var protoHeaders = csvHeaders.Select(header => new FieldId { Name = header });
    var protoRows = csvRows.Select(CsvRow =>
    {
        var rowValues = CsvRow.Split(',');
        var protoValues = rowValues.Select(RowValue =>
        {
            System.DateTime parsedDate;
            if (System.DateTime.TryParse(RowValue, out parsedDate))
            {
                return new Value
                {
                    DateValue = new Google.Type.Date
                    {
                        Year = parsedDate.Year,
                        Month = parsedDate.Month,
                        Day = parsedDate.Day
                    }
                };
            }
            else
            {
                return new Value
                {
                    StringValue = RowValue
                };
            }
        });

        var rowObject = new Table.Types.Row();
        rowObject.Values.Add(protoValues);
        return rowObject;
    });

    var dateFieldList = dateFields
        .Split(',')
        .Select(field => new FieldId { Name = field });

    // Construct + execute the request
    var dateShiftConfig = new DateShiftConfig
    {
        LowerBoundDays = lowerBoundDays,
        UpperBoundDays = upperBoundDays
    };
    bool hasKeyName = !String.IsNullOrEmpty(keyName);
    bool hasWrappedKey = !String.IsNullOrEmpty(wrappedKey);
    bool hasContext = !String.IsNullOrEmpty(contextField);
    if (hasKeyName && hasWrappedKey && hasContext)
    {
        dateShiftConfig.Context = new FieldId { Name = contextField };
        dateShiftConfig.CryptoKey = new CryptoKey
        {
            KmsWrapped = new KmsWrappedCryptoKey
            {
                WrappedKey = ByteString.FromBase64(wrappedKey),
                CryptoKeyName = keyName
            }
        };
    }
    else if (hasKeyName || hasWrappedKey || hasContext)
    {
        throw new ArgumentException("Must specify ALL or NONE of: {contextFieldId, keyName, wrappedKey}!");
    }

    var deidConfig = new DeidentifyConfig
    {
        RecordTransformations = new RecordTransformations
        {
            FieldTransformations =
            {
                new FieldTransformation
                {
                    PrimitiveTransformation = new PrimitiveTransformation
                    {
                        DateShiftConfig = dateShiftConfig
                    },
                    Fields = { dateFieldList }
                }
            }
        }
    };

    DeidentifyContentResponse response = dlp.DeidentifyContent(
        new DeidentifyContentRequest
        {
            Parent = $"projects/{projectId}",
            DeidentifyConfig = deidConfig,
            Item = new ContentItem
            {
                Table = new Table
                {
                    Headers = { protoHeaders },
                    Rows = { protoRows }
                }
            }
        });

    // Save the results
    List<String> outputLines = new List<string>();
    outputLines.Add(csvLines[0]);

    outputLines.AddRange(response.Item.Table.Rows.Select(ProtoRow =>
    {
        var Values = ProtoRow.Values.Select(ProtoValue =>
        {
            if (ProtoValue.DateValue != null)
            {
                var ProtoDate = ProtoValue.DateValue;
                System.DateTime Date = new System.DateTime(
                    ProtoDate.Year, ProtoDate.Month, ProtoDate.Day);
                return Date.ToShortDateString();
            }
            else
            {
                return ProtoValue.StringValue;
            }
        });
        return String.Join(',', Values);
    }));

    File.WriteAllLines(outputCsvFile, outputLines);

    return 0;
}

時間抽出

時間抽出(DLP API の TimePartConfig)オブジェクトを実行すると、日付、時刻、タイムスタンプに一致した値の箇所が保持されます。年、月、日など(TimePart オブジェクトに列挙される)、抽出する時間値の種類を指定します。

たとえば、時間の箇所を YEAR に抽出するように設定して timePartConfig の変換を構成した場合、次の一覧の左の列のデータを Cloud DLP に送信すると、右の列のような変換後の値が得られます。

元の値 変換後の値
9/21/1976 1976
6/7/1945 1945
1/20/2009 2009
7/4/1776 1776
8/1/1984 1984
4/21/1982 1982
このページは役立ちましたか?評価をお願いいたします。

フィードバックを送信...

Data Loss Prevention のドキュメント