データマスキングしたテストDBデータを作る


# はじめに

本番環境から取得したデータを使用する際のデータマスキング　についてまとめる

- 特定の列や値を匿名化または置換。
    - 主要なデータ項目に対し、尤もらしい形式を保持しつつ、匿名化するpython処理

# Fakerを使用した簡単な例

Fakerライブラリを使用して、リアルな偽のデータを生成します

```python

import pandas as pd
from faker import Faker

# サンプルの匿名化前のデータ
data = {
    'name': ['山田太郎', '鈴木花子', '佐藤健', '田中美穂'],
    'email': ['yamada@example.com', 'suzuki@example.com', 'sato@example.com', 'tanaka@example.com'],
    'phone': ['090-1234-5678', '080-2345-6789', '070-3456-7890', '090-4567-8901'],
    'address': ['東京都港区1-2-3', '大阪府大阪市中央区1-2-3', '愛知県名古屋市中区1-2-3', '福岡県福岡市中央区1-2-3']
}

df = pd.DataFrame(data)

# Fakerインスタンスの作成
faker = Faker('ja_JP')

# 名前の匿名化
df['name'] = [faker.name() for _ in range(len(df))]

# メールアドレスの匿名化
df['email'] = [faker.email() for _ in range(len(df))]

# 電話番号の匿名化
df['phone'] = [faker.phone_number() for _ in range(len(df))]

# 住所の匿名化
df['address'] = [faker.address() for _ in range(len(df))]

print(df)
```

出力結果
```
name                 email           phone                 address
0    小林 裕美  uekimiyuki@example.org  090-4082-8195      山梨県田村市西之園町2-12-12
1    松田 花子    kubonarumi@kudo.net  090-4747-7765  神奈川県越智郡金城町前原西5-10-12
2    岩崎 美咲   ayamiyoshi@example.jp  080-7207-7844       大阪府高松市古市町2-15-5
3  今井 奈央子  maikeyasuhiro@abe.com  090-4119-8787        青森県下高井郡沼村中央4-8
```

# Lambdaで実行する例

PythonスクリプトはAWSのLambda関数として実装し、特定のバケット内のS3から設定ファイルとデータを読み込んで、データを匿名化した後、結果を別のバケットに保存します。

```mermaid

sequenceDiagram
    participant Lambda
    participant Env
    participant S3

    Lambda->>+Env: 環境変数取得 (get_env_var)
    Env-->>-Lambda: 環境変数

    Lambda->>+Env: 設定ファイル読み込み (read_config_from_s3)
    Env-->>-Lambda: JSON設定データ

    loop 各テーブルに対して
        Lambda->>+S3: データ読み込み (load_data_from_s3)
        S3-->>-Lambda: CSVデータ (DataFrame)

        Lambda->>Lambda: Faker関数呼び出し
        Lambda-->>Lambda: 匿名化済みDataFrame

        Lambda->>+S3: データ保存 (save_data_to_s3)
        S3-->>-Lambda: 保存完了
    end

    Lambda->>Lambda: 処理完了レスポンス生成

```


```python

import json
import boto3
import os
from faker import Faker
import pandas as pd
from io import StringIO
from botocore.exceptions import ClientError

def get_env_var(var_name):
    """ 環境変数を取得し、存在しない場合はエラーを発生させる。 """
    try:
        return os.environ[var_name]
    except KeyError:
        print(f"Environment variable {var_name} not found.")
        raise

def read_config_from_s3(bucket, key):
    """ S3から設定ファイルを読み込む。 """
    s3 = boto3.client('s3')
    try:
        response = s3.get_object(Bucket=bucket, Key=key)
        return json.loads(response['Body'].read())
    except ClientError as e:
        print(f"Failed to read config from {bucket}/{key}: {e}")
        raise

def load_data_from_s3(bucket, key):
    """ S3からCSVファイルを読み込み、pandas DataFrameとして返す。 """
    s3 = boto3.client('s3')
    try:
        response = s3.get_object(Bucket=bucket, Key=key)
        return pd.read_csv(response['Body'])
    except ClientError as e:
        print(f"Failed to load data from {bucket}/{key}: {e}")
        raise

def save_data_to_s3(df, bucket, key):
    """ DataFrameをCSVとしてS3に保存する。 """
    csv_buffer = StringIO()
    df.to_csv(csv_buffer, index=False)
    s3_resource = boto3.resource('s3')
    try:
        s3_resource.Object(bucket, key).put(Body=csv_buffer.getvalue())
    except ClientError as e:
        print(f"Failed to save data to {bucket}/{key}: {e}")
        raise

def anonymize_data(df, columns, faker_functions):
    """ DataFrame内の指定された列を匿名化する。 """
    faker = Faker('ja_JP')
    for column, faker_function in zip(columns, faker_functions):
        function, params = faker_function['name'], faker_function.get('parameters', {})
        df[column] = [getattr(faker, function)(**params) for _ in range(len(df))]

def handler(event, context):
    """ Lambdaのメインハンドラ関数。 """
    try:
        input_bucket = get_env_var('INPUT_BUCKET')
        output_bucket = get_env_var('OUTPUT_BUCKET')
        config_key = get_env_var('CONFIG_KEY')

        config = read_config_from_s3(input_bucket, config_key)

        for table in config['tables']:
            df = load_data_from_s3(input_bucket, table['input_file'])
            anonymize_data(df, [col['name'] for col in table['columns']], [col['faker_function'] for col in table['columns']])
            save_data_to_s3(df, output_bucket, table['output_file'])

        return {'statusCode': 200, 'body': json.dumps('Data anonymization completed successfully.')}
    except Exception as e:
        return {'statusCode': 500, 'body': json.dumps(f"An error occurred: {str(e)}")}

```

```nonymization_config.json

{
  "tables": [
    {
      // ユーザーテーブル用の設定
      "input_file": "user_table.csv",  // 入力ファイル名
      "output_file": "anonymized_user_table.csv",  // 匿名化後の出力ファイル名
      "columns": [
        // 各列の匿名化方法の指定
        {"name": "name", "faker_function": "name"},  // 名前
        {"name": "last_name", "faker_function": "last_name"},  // 苗字
        {"name": "first_name", "faker_function": "first_name"},  // 名前
        {"name": "address", "faker_function": "address"},  // 住所
        {"name": "zipcode", "faker_function": "zipcode"},  // 郵便番号
        {"name": "city", "faker_function": "city"},  // 市区町村
        {"name": "prefecture", "faker_function": "prefecture"},  // 都道府県
        {"name": "email", "faker_function": "email"},  // メールアドレス
        {"name": "phone", "faker_function": "phone_number", "parameters": {"formats": "+81-##-####-####"}},  // 電話番号
        {"name": "ssn", "faker_function": "ssn"},  // 社会保険番号
        {"name": "university", "faker_function": "university"},  // 大学名
        {"name": "university_name", "faker_function": "university_name"},  // 大学名
        {"name": "secondary_school", "faker_function": "secondary_school"},  // 中学校
        {"name": "credit_card_number", "faker_function": "credit_card_number", "parameters": {"card_type": "Visa"}},  // クレジットカード番号
        {"name": "credit_card_expiry", "faker_function": "credit_card_expiry", "parameters": {"format": "MM/YY"}},  // クレジットカード有効期限
        {"name": "credit_card_provider", "faker_function": "credit_card_provider"}  // クレジットカード提供者
      ]
    },
    {
      // 販売店マスターテーブル用の設定
      "input_file": "merchant_master_table.csv",  // 入力ファイル名
      "output_file": "anonymized_merchant_master_table.csv",  // 匿名化後の出力ファイル名
      "columns": [
        // 各列の匿名化方法の指定
        {"name": "name", "faker_function": "company"},  // 会社名
        {"name": "email", "faker_function": "company_email"},  // 会社メールアドレス
        {"name": "phone", "faker_function": "phone_number", "parameters": {"formats": "+81-##-####-####"}},  // 会社電話番号
        {"name": "address", "faker_function": "address"},  // 会社住所
        {"name": "credit_card_number", "faker_function": "credit_card_number", "parameters": {"card_type": "Visa"}},  // 会社クレジットカード番号
        {"name": "url", "faker_function": "url"}  // 会社ウェブサイトURL
      ]
    },
    {
      // トランザクションテーブル用の設定
      "input_file": "transaction_table.csv",  // 入力ファイル名
      "output_file": "anonymized_transaction_table.csv",  // 匿名化後の出力ファイル名
      "columns": [
        // 各列の匿名化方法の指定
        {"name": "transaction_id", "faker_function": "uuid4", "parameters": {"version": 4}},  // トランザクションID
        {"name": "transaction_date", "faker_function": "date_between", "parameters": {"start_date": "-5y", "end_date": "today"}}  // トランザクション日付
      ]
    }
  ]
}

```