第10章 ありがちな失敗と直し方 — セキュリティ落とし穴集
📖 この章のゴール:初心者がやりがちなセキュリティの失敗を一覧で知り、それぞれの直し方が言えるようになる。これまでの章の⚠️(ハマりどころ)を、ここで総ざらいします。 ← 目次・はじめにへもどる
📱 ニュースでよく見る「個人情報流出」
「○○社、利用者の個人情報が流出」——こういうニュースを見たことがありますよね。
イメージとしては、天才ハッカーが何日もかけて鉄壁のシステムを突破した……みたいな話を思い浮かべがちです。でも、実際の流出の多くは、もっと地味な原因です。
🚪 たとえ話:玄関のカギをかけ忘れた家 泥棒が窓を割って入ったのではなく、玄関のカギをかけ忘れていただけ——そんな事故がとても多いのです。
つまり、ほとんどは 派手なハッキングではなく、単純な設定ミス。そして、そのミスの多くは、この教材ですでに「⚠️ ハマりどころ」として出てきたものです。
この章は 失敗カタログです。各項目を「何が起きる → どう直す」の形で並べます。むずかしさの目印(🟢 基礎 / 🔧 応用)も付けます。公開前に、上から順にチェックしてください。
1. RLSを切ったまま公開(全データ流出)🟢
何が起きる: テーブルを作っただけ、あるいは RLS(行レベルセキュリティ)を有効にしていない と、そのテーブルは 全行が誰からでも丸見え になります。anon key はもともと公開してOKな鍵(RLSで守る前提)なので、RLSが無いと「カギなしの倉庫」と同じです。他人の下書きも、全員ぶん筒抜けです。
🔧 どう直す:
- すべてのテーブルで
enable row level securityを有効にする。- その上で「持ち主だけが触れる」ポリシーを書く(第4章 04-data-isolation)。
- 「RLSを有効にしたのに、ポリシーを1つも書いていない」と、今度は自分でも何も読めなくなるので、ポリシーまでがワンセットです。
2. フロントのifだけで隠して満足 🟢
何が起きる:
画面のコードで「自分のじゃないから表示しない」と if で隠しても、それは見た目を隠しているだけ。APIの窓口を直接叩かれたら、データはそのまま返ってきます(第5章 05-api-design)。
🚪 玄関にカーテンを引いても、ドアにカギがかかっていなければ意味がない、という話です。
🔧 どう直す:
- 出し分け(誰に何を見せるか)と制限は、サーバー側のRLSでかける。
- フロントの
ifは「見た目を整える専用」と割り切る。守りの本体はサーバー側です。
3. 秘密の鍵をフロントに置く/gitにコミット 🟢
何が起きる: Supabaseには2種類の鍵があります。
- anon key:公開してOK(RLSで守る前提の鍵)。フロントに置いてよい。
- service_role key:全権限の鍵。RLSをすべて素通りします。絶対に秘密。
この service_role key をフロントのコードに書く と、ブラウザを開いた人全員に「なんでもできる鍵」を配ってしまうのと同じです。さらに 鍵をgitにコミットすると、リポジトリを見られた瞬間に漏れます。
🔧 どう直す:
- service_role key などの秘密は、サーバー側/環境変数に置く。フロントには絶対に入れない。
- 鍵を gitに入れない(
.gitignoreで除外)。- もし誤ってコミット・公開してしまったら、すぐにローテーション(鍵を作り直して無効化) する。一度ネットに出た鍵は「もう漏れたもの」として扱います。
4. ユーザー入力をそのまま画面に出す(XSS)🟢
何が起きる: 人が入力したツイート本文(第3章 03-first-data-save)を、そのまま画面に差し込むと、入力に紛れ込ませたスクリプト(プログラム)が実行されてしまいます。これを XSS(クロスサイト・スクリプティング) と呼びます。悪意ある人が、他人のブラウザで勝手にコードを動かせてしまう、という事故です。
🔧 どう直す:
- 入力は エスケープして表示する(文字を「ただの文字」として扱い、コードとして動かさない)。多くの画面ライブラリ(React / Vue など)は標準でこれをやってくれます。
- 危険なHTML差し込み(Vueの
v-html、innerHTMLなど)を避ける。どうしても必要なときだけ、無害化(サニタイズ)してから使う。
5. 個人データを共有キャッシュに置く 🟢
何が起きる: 速くするためのキャッシュ(第9章「キャッシュ」)に、個人ごとに違うデータ(自分のホームタイムラインなど)を、みんなで共有するキャッシュに入れてしまうと、他人にあなたのタイムラインが見えてしまいます。
🧊 みんなで使う冷蔵庫に、名前を書いた自分のお弁当を入れたら、他の人も開けて見られる、という話です。
🔧 どう直す:
- 個人データのキャッシュは private(その人専用) にする。
- 共有キャッシュ(CDNなど、みんなで使う置き場)には個人データを入れない。入れてよいのは「全員に同じでよいもの」だけ。
6. SQLを文字列連結(SQLインジェクション)🔧
何が起きる: ユーザーの入力を、SQLの命令文に文字列としてそのままつなげると、入力に紛れ込ませた命令でデータベースを改ざん・盗み見されます。これが SQLインジェクションです。
危険:入力をそのまま命令文につなぐ
"SELECT * FROM users WHERE name = '" + 入力 + "'"
→ 入力に細工をされると、命令の意味が書き換わる 😱
🔧 どう直す:
- Supabaseクライアントを使う(
.eq()などは値を安全に扱ってくれる)。- 生のSQLを書くときは プレースホルダ(値を後から渡す仕組み) を使い、値を文字列として命令文に埋め込まない。「命令」と「値」を混ぜないのがコツです。
7. 連番IDで列挙(enumeration)🔧
何が起きる:
ユーザーのURLが /users/1、/users/2、/users/3 … と連番だと、数字を1ずつ増やすだけで全ユーザーを総当たりで一覧できてしまいます。これを 列挙(enumeration) と呼びます(第2章 02-what-is-a-user で「連番を避けてUUID」と学びましたね)。
🔧 どう直す:
- IDは UUID(推測しにくいランダムなID)にする。
- その上で 権限(RLS) でちゃんと守る。「IDが当てられないこと」を守りの本体にしない——UUIDは「めくりにくくする」だけで、本当の鍵はあくまで権限です。
8. N+1で激重 🔧
何が起きる: 一覧を表示するときに、各行ごとに追加の問い合わせをデータベースに投げてしまうパターンです。投稿が100件あれば、一覧で1回+各投稿の作者を取りに100回=合計101回問い合わせる、といった具合に激重になります(第8章「ホームタイムライン」)。これを N+1問題 と呼びます。
🔧 どう直す:
- まとめて取得する。関連データは
join(テーブルをつないで一度に取る)や「まとめ取り」で、問い合わせ回数を減らす。- 「ループの中でDBに問い合わせていないか?」を疑うのが、見つけるコツです。
もっと広く学ぶなら
ここで挙げたのは「Twitterクローンでよく出る」ものだけです。攻撃する側の視点から、どんな手口があるかを体系的に知りたくなったら、姉妹コンテンツへどうぞ。
- セキュリティ脅威マップ(攻撃視点の分類ハブ) — 「どう攻められるか」を分類して見渡せるハブ
⚠️ まとめ:公開前チェックリスト
アプリを世に出す前に、最低限ここを確認しましょう。
- ☑ RLS:すべてのテーブルでRLSを有効化+持ち主ポリシーを書いた(1・2)
- ☑ 秘密鍵:service_role key がフロント/gitに無い。anon keyはRLS前提で公開OK(3)
- ☑ XSS:ユーザー入力をそのまま画面に出していない(エスケープ済み・危険なHTML差し込みなし)(4)
- ☑ キャッシュ:個人データを共有キャッシュに入れていない(5)
- ☑ 権限:守りを「IDの秘匿」や「フロントの
if」に頼らず、サーバー側の権限(RLS)で守っている(2・7)
🔑 共通する教訓は1つ。守りの本体は、いつもサーバー側(RLS・権限)に置く。 画面の隠しや、IDの当てにくさは「おまけ」にすぎません。
🤖 AIに頼むなら(Vibe codingのコツ)
AIにコードを書かせたら、セキュリティ観点のレビューもAIに頼むと効率的です。
🗣 プロンプト例: 「このコードをセキュリティ観点でレビューして。とくに次を確認して: ① 全テーブルでRLSが有効か(無効のテーブルが無いか) ② service_role key がフロントに無いか(秘密鍵の漏れ) ③ XSS(ユーザー入力をそのまま画面に出していないか) ④ 認可漏れ(他人のデータを触れないか) ⑤ SQLインジェクション(入力を命令文に直接つないでいないか)」
⚠️ AIの指摘は便利ですが、最終確認は人間です。「なぜ危ないのか」をこの章で理解しておくと、AIの答えが正しいか判断できます。
📝 ことばメモ
- RLS(行レベルセキュリティ):データベース側で「どの行を誰が触れるか」を決める仕組み。守りの本体
- service_role key / anon key:service_role keyは全権限・絶対に秘密。anon keyは公開OK(RLSで守る前提)
- XSS(クロスサイト・スクリプティング):ユーザー入力に紛れたスクリプトが、他人のブラウザで実行されてしまう攻撃
- SQLインジェクション:入力をSQL命令文に直接つなぐことで、命令を書き換えられてしまう攻撃
- 最小権限:必要な分だけの権限しか与えないこと。広すぎる権限は事故のもと
- 列挙(enumeration):連番IDなどを1つずつ試して、全データを総当たりで一覧されること
➡️ 次の章へ
ここまでで「自分で作るときの落とし穴」が分かりました。次の第11章では、いよいよ AIにコードを作らせて、それを”読む” 練習をします。この章のチェックリストが、AIが書いたコードの安全・危険を見抜く目になります。