この記事は デベロッパー プログラム エンジニア、Nicolas Garnier による The Firebase Blog の記事 "Content Moderation with Cloud Functions for Firebase" を元に翻訳・加筆したものです。詳しくは元記事をご覧ください。
掲示板、ソーシャル ネットワーク、ブログ プラットフォームなど、ユーザーがコンテンツを投稿できるアプリでは、常に不適切なコンテンツが公開されるリスクが存在します。本投稿では、Firebase アプリで
Cloud Functions を使って自動的に不適切なコンテンツを無害化する方法について紹介します。
コンテンツを無害化するもっとも一般的な戦略は、「受動的な無害化」です。通常は、ユーザーに不適切なコンテンツを報告してもらうためのリンクを追加し、ルールに従っていないコンテンツを手動で確認して削除します。しかし、受動的な無害化を補完するために自動的な無害化の仕組みを追加すると、不適切なコンテンツが公開されることを防ぐ仕組みをさらに強化できます。Cloud Functions を使うと、Firebase アプリでユーザーが公開した不適切なテキストや写真などのコンテンツに対する自動チェックを簡単に行うことができます。どのくらい簡単にできるのか、早速見てみましょう。
ここでは、2 種類の自動コンテンツ無害化を実行します。
テキストの無害化では、汚い言葉やシャウト(「SHOUTING!!!」など、すべて大文字の言葉)をすべて削除します。
画像の無害化では、アダルト コンテンツや暴力的なコンテンツにぼかしをかけます。
本質的に、自動的な無害化は信頼できる環境(つまり、クライアント以外)で行う必要があります。そのため、Cloud Functions for Firebase はまさに適任です。2 種類の無害化を行うには、2 つの機能が必要です。
テキストの無害化
テキストの無害化では、Firebase Realtime Database をトリガーとして
moderator
という名前の機能を実行します。ユーザーが Realtime Database に新しくコメントや投稿を追加すると、
bad-words npm パッケージを使って汚い言葉を削除する機能が実行されます。続いて、
capitalize-sentence npm パッケージを使って、大文字が多用されているメッセージ(通常は、ユーザーによるシャウト)を小文字に修正します。最後のステップでは、無害化したメッセージを Realtime Database に書き戻します。
実例として、ユーザーがチャットルームに書き込んだメッセージのリストを表す単純なデータ構造を考えてみましょう。メッセージは 1 つの
text
属性を含むオブジェクトで、
/messages
リストに格納されています。
/functions-project-12345
/messages
/key-123456
text: "This is my first message!"
/key-123457
text: "IN THIS MESSAGE I AM SHOUTING!!!"
新しく追加されたメッセージに対して機能が実行されると、2 つの属性が追加されます。無害化機能によってメッセージが検証された際に
true
になる
sanitized
属性と、メッセージで不適切なコンテンツが検知され、修正された場合に
true
になる
moderated
属性です。たとえば、上の 2 つのメッセージに対して無害化機能が実行されると、次のようになります。
/functions-project-12345
/messages
/key-123456
text: "This is my first message!",
sanitized: true,
moderated: false
/key-123457
text: "In this message I am shouting."
sanitized: true,
moderated: true
moderator
機能は、メッセージが書き込まれるたびに実行されます。これは、
functions.database().path('/messages/{messageId}').onWrite(...)
トリガールールを使用して設定します。ここでは、メッセージを無害化し、そのメッセージを Realtime Database に書き戻しています。
exports.moderator = functions.database.ref('/messages/{messageId}')
.onWrite(event => {
const message = event.data.val();
if (message && !message.sanitized) {
// Retrieved the message values.
console.log('Retrieved message content: ', message);
// Run moderation checks on on the message and moderate if needed.
const moderatedMessage = moderateMessage(message.text);
// Update the Firebase DB with checked message.
console.log('Message has been moderated. Saving to DB: ', moderatedMessage);
return event.data.adminRef.update({
text: moderatedMessage,
sanitized: true,
moderated: message.text !== moderatedMessage
});
}
});
moderateMessage
関数では、まずユーザーがシャウトしているかどうかをチェックし、シャウトしている場合は文を小文字に変換します。次に、
bad-words
パッケージ フィルタを使って汚い言葉をすべて削除します。
function moderateMessage(message) {
// Re-capitalize if the user is Shouting.
if (isShouting(message)) {
console.log('User is shouting. Fixing sentence case...');
message = stopShouting(message);
}
// Moderate if the user uses SwearWords.
if (containsSwearwords(message)) {
console.log('User is swearing. moderating...');
message = moderateSwearwords(message);
}
return message;
}
// Returns true if the string contains swearwords.
function containsSwearwords(message) {
return message !== badWordsFilter.clean(message);
}
// Hide all swearwords. e.g: Crap => ****.
function moderateSwearwords(message) {
return badWordsFilter.clean(message);
}
// Detect if the current message is shouting. i.e. there are too many Uppercase
// characters or exclamation points.
function isShouting(message) {
return message.replace(/[^A-Z]/g, '').length > message.length / 2 || message.replace(/[^!]/g, '').length >= 3;
}
// Correctly capitalize the string as a sentence (e.g. uppercase after dots)
// and remove exclamation points.
function stopShouting(message) {
return capitalizeSentence(message.toLowerCase()).replace(/!+/g, '.');
}
注: bad-words パッケージは
badwords-list パッケージの
汚い言葉のリストを使いますが、これには 400 個の言葉しか含まれていません。ご存じのように、ユーザーの想像力は無制限なので、これはすべてを網羅したリストではありません。そのため、汚い言葉の辞書を拡張する必要があるかもしれません。
画像の無害化
画像の無害化を行うために、Cloud Storage にファイルがアップロードされるたびに実行される
blurOffensiveImages
機能を設定します。これは、
functions.cloud.storage().onChange(...)
トリガールールを使用して設定します。ここでは、
Google Cloud Vision API を使って画像に暴力的なコンテンツやアダルト コンテンツが含まれているかをチェックしています。Cloud Vision API には、画像内の
不適切なコンテンツを検知することに特化した機能があります。画像が不適切だった場合は、画像にぼかしをかけます。
exports.blurOffensiveImages = functions.storage.object().onChange(event => {
const object = event.data;
const file = gcs.bucket(object.bucket).file(object.name);
// Exit if this is a move or deletion event.
if (object.resourceState === 'not_exists') {
return console.log('This is a deletion event.');
}
// Check the image content using the Cloud Vision API.
return vision.detectSafeSearch(file).then(data => {
const safeSearch = data[0];
console.log('SafeSearch results on image', safeSearch);
if (safeSearch.adult || safeSearch.violence) {
return blurImage(object.name, object.bucket, object.metadata);
}
});
});
Cloud Storage に格納されている画像をぼかすには、まず Cloud Functions インスタンスのローカルに画像をダウンロードします。そして、ImageMagick で画像をぼかし、Cloud Storage に再アップロードします。ImageMagick はすべてのインスタンスにデフォルトでインストールされています。
function blurImage(filePath, bucketName, metadata) {
const filePathSplit = filePath.split('/');
filePathSplit.pop();
const fileDir = filePathSplit.join('/');
const tempLocalDir = `${LOCAL_TMP_FOLDER}${fileDir}`;
const tempLocalFile = `${LOCAL_TMP_FOLDER}${filePath}`;
const bucket = gcs.bucket(bucketName);
// Create the temp directory where the storage file will be downloaded.
return mkdirp(tempLocalDir).then(() => {
console.log('Temporary directory has been created', tempLocalDir);
// Download file from bucket.
return bucket.file(filePath).download({
destination: tempLocalFile
});
}).then(() => {
console.log('The file has been downloaded to', tempLocalFile);
// Blur the image using ImageMagick.
return exec(`convert ${tempLocalFile} -channel RGBA -blur 0x8 ${tempLocalFile}`);
}).then(() => {
console.log('Blurred image created at', tempLocalFile);
// Uploading the Blurred image.
return bucket.upload(tempLocalFile, {
destination: filePath,
metadata: {metadata: metadata} // Keeping custom metadata.
});
}).then(() => {
console.log('Blurred image uploaded to Storage at', filePath);
});
}
Cloud Functions for Firebase は、ユーザーの操作に応答して自動的に無害化ルールを適用できる優れたツールです。
テキストの無害化と
画像の無害化のサンプルはオープンソースです。自由にご覧ください。
Posted by
Khanh LeViet - Developer Relations Team