事例・基礎知識
第2のNauNauにならないために。位置情報アプリを作るなら知っておくべきFirestore/Supabaseのセキュリティ設定
位置情報共有アプリNauNauで発生した情報漏洩。その原因であるデータベースのセキュリティ設定(RLS/Security Rules)について、位置情報アプリを作る開発者が絶対に知っておくべき対策を解説します。
「友達の居場所がリアルタイムでわかる」というコンセプトで、Z世代を中心に数百万人規模のユーザーを集めた位置情報共有アプリ「NauNau」。しかし2023年、ユーザー情報が外部から閲覧可能な状態にあったことが発覚し、サービスは一時停止。その後、運営会社は事業を譲渡することになりました。
このインシデントが示すのは、「データベースのセキュリティ設定を間違えると、アプリの画面上は正常に動いていても、裏側では全データが誰でも見られる状態になる」という事実です。
技術的に何が起きたのか
Firebase(Firestore)やSupabaseのようなBaaS(Backend as a Service)には、データベースへのアクセスを制御するルール設定があります。Firestoreでは「Security Rules」、Supabaseでは「RLS(Row Level Security)」と呼ばれるものです。
問題のある設定とはどういうものか。Firestoreでいえば、以下のようなルールです。
// 危険な設定例(絶対にこのままリリースしてはいけない)
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read, write: if true; // 誰でも読み書きできる
}
}
}
allow read: if true という記述は「誰でも読める」という意味です。これはFirebaseの初期設定やチュートリアルに登場することがあり、「開発中は便利だから」と使い続けてそのままリリースしてしまうケースが後を絶ちません。
Supabaseの場合は、テーブルを作成した際にRLSがデフォルトで有効になっていますが、ポリシーを一つも設定しないとすべてのリクエストがブロックされます。開発中に「なんか取れない」と焦ったユーザーがRLSを無効化(Disable RLS)してしまうことがよくあります。
なぜAI生成コードでこれが頻発するのか
AIに「Supabaseでユーザーの位置情報を保存・取得するコードを書いて」と依頼すると、動作するコードは返ってきます。しかしセキュリティルールまで含めてレビューしてくれるかどうかは、プロンプトの書き方次第です。
多くの場合、AIは「動作するコード」として RLS disabled の状態や allow read: if true のルールを含むサンプルを出力します。それは「チュートリアルとして動作する最速のコード」であり、「本番で安全なコード」ではありません。
位置情報漏洩の特別な危険性
位置情報の漏洩は、他の個人情報漏洩と一線を画す深刻さがあります。
氏名やメールアドレスが漏洩しても、直接的な物理的危険につながることは稀です。しかし位置情報は違います。「このユーザーが毎朝8時に自宅を出て、夜11時に帰宅する」「今この瞬間どこにいるか」がわかるデータは、ストーキング・つきまとい・家庭内暴力のツールに直結します。
DVや虐待から逃げているユーザー、匿名でサービスを使いたいユーザーが被害を受ける可能性を真剣に考える必要があります。これは「個人情報保護法への対応」という法的問題であるだけでなく、利用者の安全に関わる倫理的問題です。
Supabaseでの正しいRLS設定
まずRLSを有効化する
Supabaseのダッシュボードで該当テーブルを開き、「RLS enabled」の状態であることを確認してください。
-- SQLでも有効化できる
ALTER TABLE locations ENABLE ROW LEVEL SECURITY;
「自分のデータだけ読める」ポリシーを設定する
最も基本的なRLSポリシーです。ログインユーザーが自分のデータだけを読み書きできます。
-- 自分の位置情報だけ読める
CREATE POLICY "Users can view own location"
ON locations FOR SELECT
USING (auth.uid() = user_id);
-- 自分の位置情報だけ更新できる
CREATE POLICY "Users can update own location"
ON locations FOR UPDATE
USING (auth.uid() = user_id);
「フレンド同士は見られる」ポリシー
位置情報共有アプリの場合、フレンド関係にあるユーザー同士は互いの位置を見られる必要があります。
-- friendshipsテーブルが存在する前提
CREATE POLICY "Friends can view each other's location"
ON locations FOR SELECT
USING (
auth.uid() = user_id
OR EXISTS (
SELECT 1 FROM friendships
WHERE (user_id = auth.uid() AND friend_id = locations.user_id)
AND status = 'accepted'
)
);
Firebase Security Rulesでの同等の設定
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /locations/{userId} {
// 本人だけが読み書きできる
allow read, write: if request.auth != null && request.auth.uid == userId;
// フレンドも読める場合
allow read: if request.auth != null && (
request.auth.uid == userId ||
exists(/databases/$(database)/documents/friendships/$(request.auth.uid + '_' + userId))
);
}
}
}
「自分しか使わない時は気づけない」という罠
開発中、自分一人でテストしていると、データの混線には絶対に気づけません。自分のデータを書いて、自分のアカウントで読む。当然、自分のデータが返ってくる。正常に動いているように見えます。
しかし2人目のテストユーザーを作って試すと初めてわかります。AさんがBさんの位置情報を取得できてしまう、あるいはBさんのデータがAさんの画面に表示される——これが「データ混線」です。
テスト方法: ブラウザのシークレットウィンドウや別のデバイスを使い、異なるアカウントでログインした状態でAPIを直接叩いてみてください。curl や Supabase の「Table Editor」から直接クエリを実行して、他ユーザーのデータが取得できないことを確認します。
センシティブなデータを扱うアプリのリリース前チェックリスト
位置情報に限らず、健康データ・金融情報・チャット履歴など、センシティブなデータを扱うアプリでは、リリース前に以下を必ず確認してください。
- RLS(またはSecurity Rules)が全テーブルで有効になっているか
- 「デフォルト拒否」の原則が適用されているか(明示的に許可していないものは拒否)
- 2つ以上の異なるアカウントを使ったクロステストを実施したか
- 直接APIリクエストを送って、他ユーザーのデータが取得できないか確認したか
- データの暗号化が必要な項目を特定し、適切に暗号化しているか
- ログ・監査証跡が残るようになっているか
「画面が動いた = 完成」ではありません。位置情報アプリにおいて、セキュリティの不備はユーザーの身の安全に直結します。リリース前のセキュリティ確認は、技術的な義務でもあり、倫理的な責任でもあります。
Supabaseを使っている場合、Supabaseセキュリティ診断でアプリのURLを入れるだけでデータ露出リスクを自動チェックできます。公開前の確認にお使いください。
→ AI生成コードのセキュリティリスク全般はAI生成コードのセキュリティリスク10選を参照
→ 非エンジニア向けの最低限の対策はVibe Codingで最低限やるべきセキュリティ対策にまとめている