第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-htmlinnerHTML など)を避ける。どうしても必要なときだけ、無害化(サニタイズ)してから使う。

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が書いたコードの安全・危険を見抜く目になります。

← 目次・はじめにへもどる