Amazon Comprehendを使ってS3にアップロードしたテキストの個人情報をマスキングしてみる

スポンサーリンク
ハンズオン
スポンサーリンク

はじめに

AWSのAIサービスを使ったハンズオンです。

メールで誤って個人情報を本文に記載してしまうセキュリティ事故は珍しくありません。

実際、メールや文書の内容を流用する際、すべてを人間の目でチェックするのは見落としが発生しやすいのが現状です。

今回紹介するAmazon ComprehendはテキストをAIで解析してくれるサービスで、1つ機能として個人情報(PII)を検出することができます。

2024/12/20時点では英語とスペイン語に対応しており、日本語にはまだ対応していません。

今回はAmazon Comprehendの個人情報(PII)検出機能を使って、英語の文章から個人情報を検出してみます。

このハンズオンでは、Amazon Comprehendを使って、S3にアップロードした英語のテキストファイルを分析し、個人情報があればマスキングして別のS3に格納するサービスを作成していきます。

前提条件

本ハンズオンでは、簡略化のため VPC を使用していません

内部的な通信を利用する場合はS3とComprehendのエンドポイントをVPCに作成してください。

環境構成

本ハンズオンの環境構成は以下の通りです。

料金目安

本ハンズオンの料金目安です。東京リージョン(ap-northeast-1)を利用する想定で計算しています。

サービス名 前提 無料枠
あり
無料枠
なし
S3 1KBのファイルを1日保管 <$0.001 <$0.001
Lambda 3秒の処理を10回実行 $0 <$0.001
Amazon Comprehend 1000文字分の文章を分析 $0 $0.001

無料枠あり/なしに関わらずほぼ無料です。

1. S3バケットの作成

AWS管理コンソールから「S3」の画面を開き、「バケットの作成」を選択します。

個人情報分析対象のテキストを格納するバケットと個人情報をマスキングしたテキストを格納するS3バケットを作成します

※Lambda関数と同じリージョンで作成してください。

3. Lambda関数の作成

AWS管理コンソールから「Lambda」の画面を開き、「関数の作成」を選択します。

任意の関数名、ランタイム:Node.js 22.x、アーキテクチャ:x86_64として、関数を作成します。

関数が作成できたら、設定タブを選択してください。一般設定から「編集」を選択します。

基本設定の画面が表示されます。タイムアウト値はデフォルトの3秒のままでもいいですが、分析する文章量によって数値を調整してください。

実行ロールにS3バケットとComprehendの実行権限を付与します。「IAMコンソールで****ロールの表示」を選択してください。

ロールの設定画面が表示されます。許可を追加から「インラインポリシーを作成」を選択してください。

ポリシーエディタが表示されます。JSONを選択して以下のポリシーを入力してください。入力後「次へ」を選択します。

{
	"Version": "2012-10-17",
	"Statement": [
		{
			"Effect": "Allow",
			"Action": "s3:GetObject",
			"Resource": "arn:aws:s3:::【分析対象のテキストを格納するバケット名】/*"
		},
		{
			"Effect": "Allow",
			"Action": "s3:PutObject",
			"Resource": "arn:aws:s3:::【個人情報をマスキングしたテキストを格納するS3バケット名】/*"
		},
		{
			"Effect": "Allow",
			"Action": "comprehend:DetectPiiEntities",
			"Resource": "*"
		}
	]
}

確認して作成の画面で「ポリシーを作成」を選択します。

Lambdaの画面に戻って環境変数の追加をします。設定タブ>左メニューの「環境変数」を選択し「編集」を選択します。

環境変数を設定します。

キー
REGION リージョン名(ap-northeast-1など)
TARGETBUCKET 【個人情報をマスキングしたテキストを格納するS3バケット名】

以下のソースを反映し、「index.mjs」を「index.js」に名前を変えてください。

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.handler = void 0;
const client_s3_1 = require("@aws-sdk/client-s3");
const client_comprehend_1 = require("@aws-sdk/client-comprehend");
// S3クライアントとComprehendクライアントの初期化
const s3Client = new client_s3_1.S3Client({ region: process.env.REGION });
const comprehendClient = new client_comprehend_1.ComprehendClient({ region: process.env.REGION });
// S3の読み取り用ヘルパー関数
const streamToString = async (stream) => {
    const chunks = [];
    for await (const chunk of stream) {
        chunks.push(chunk);
    }
    return Buffer.concat(chunks).toString('utf-8');
};
// PIIエンティティをマスキングする関数
const maskPiiEntities = (text, entities) => {
    let maskedText = text;
    // エンティティを降順でソートして置換処理の衝突を防ぐ
    entities.sort((a, b) => b.BeginOffset - a.BeginOffset);
    for (const entity of entities) {
        const { BeginOffset, EndOffset } = entity;
        if (BeginOffset !== undefined && EndOffset !== undefined) {
            maskedText =
                maskedText.slice(0, BeginOffset) +
                    '*'.repeat(EndOffset - BeginOffset) +
                    maskedText.slice(EndOffset);
        }
    }
    return maskedText;
};
// Lambdaハンドラー
const handler = async (event) => {
    // イベントからバケット名とオブジェクトキーを取得
    const sourceBucket = event.Records[0].s3.bucket.name;
    const sourceKey = decodeURIComponent(event.Records[0].s3.object.key.replace(/\+/g, ' '));
    // S3からテキストファイルを取得
    const getObjectParams = { Bucket: sourceBucket, Key: sourceKey };
    const getObjectCommand = new client_s3_1.GetObjectCommand(getObjectParams);
    const s3Response = await s3Client.send(getObjectCommand);
    const fileContent = await streamToString(s3Response.Body);
    console.log('Original Content:', fileContent);
    // Amazon Comprehendで感情分析を実行
    const comprehendParam = {
        Text: fileContent,
        LanguageCode: "en", // 2024/12/20時点で日本語は対応していない
    };
    const comprehendCommand = new client_comprehend_1.DetectPiiEntitiesCommand(comprehendParam);
    const response = await comprehendClient.send(comprehendCommand);
    const piiEntities = response.Entities || [];
    console.log("Detect PII result:", piiEntities);
    let maskedContent = fileContent;
    if (piiEntities.length > 0) {
        console.log(`PIIが検出されました: ${sourceKey}`);
        console.log(JSON.stringify(piiEntities, null, 2));
        maskedContent = maskPiiEntities(fileContent, piiEntities);
        const outputKey = `masked/${sourceKey}`;
    }
    else {
        console.log(`PIIが検出されませんでした: ${sourceKey}`);
    }
    // 新しいS3バケットにアップロード
    const targetBucket = process.env.TARGETBUCKET; // 変換後のファイルを保存するバケット名
    const targetKey = `results/${sourceKey.replace(".txt", "")}_masked.txt`;
    // 分析結果をJSON形式でS3に保存
    const putObjectParams = {
        Bucket: targetBucket,
        Key: targetKey,
        Body: maskedContent,
        ContentType: "text/plain",
    };
    const putObjectCommand = new client_s3_1.PutObjectCommand(putObjectParams);
    await s3Client.send(putObjectCommand);
};
exports.handler = handler;

反映と名前の変更が完了したら、「Deploy」を選択します。

S3のアップロードをトリガーとする設定を追加します。「トリガーを追加」を選択します。

トリガーの設定でS3を選択し、バケットは【分析対象のテキストを格納するバケット名】を入力してください。イベントタイプは「PUT」、サフィックスは「.txt」を入力して「追加」を選択します。

3. 動作確認

以下の3ファイルを使って個人情報が正しくマスキングされるか確認してみます。

1.個人情報が多いテキスト.txt

オリジナル

My name is Jane Smith, and my phone number is +1-555-123-4567. 
You can contact me via email at jane.smith@example.com. 
I currently reside at 123 Elm Street, Springfield, USA.

マスキング後

My name is **********, and my phone number is ***************. 
You can contact me via email at **********************. 
I currently reside at ********************************.

名前、電話番号、メールアドレス、住所がマスキングされていますね。

2.個人情報が少しだけ入っているテキスト.txt

オリジナル

I recently moved to a new city and started a new job. 
My email address for work-related queries is work.email@example.com. 
It's been an exciting journey so far.

マスキング後

I recently moved to a new city and started a new job. 
My email address for work-related queries is **********************. 
It's been an exciting journey so far.

こちらもメールアドレスがマスキングされています。

3.個人情報なしテキスト.txt

オリジナル

It was a sunny day, and I decided to go for a walk in the park. 
The trees were green, and the flowers were blooming. 
It felt refreshing to spend some time outdoors.

マスキング後

It was a sunny day, and I decided to go for a walk in the park. 
The trees were green, and the flowers were blooming. 
It felt refreshing to spend some time outdoors.

こちらの文章には個人情報が含まれていないのでオリジナルの文書のまま出力されています。

まとめ

このハンズオンではテキストを解析できるAmazon Comprehendを使ってみました。

こちらの機能を応用することで、メールや文書に個人情報が含まれていないか機械的にチェックすることができたり、データやログに個人情報が含まれていたらマスキングをするといった管理が可能になります。

執筆時点で日本語対応はしていませんが、将来的に日本語対応されることを期待しています。

以下AWSのAIサービス関連のおすすめ書籍です。

 

 

タイトルとURLをコピーしました