付録C 普通のDB(PostgreSQL / MySQL)入門 — RLSがない世界での守り方
📖 このページのゴール:Supabase(RLS)の外=一般的なデータベースでは、「自分のデータだけ」をどうやって守るのかを知る。 ← 目次・はじめにへもどる
本編の第4章では、Supabaseの RLS という機能で「自分の行だけ」を守りました。でも、世の中のアプリが全部RLSを使っているわけではありません。RLSがない場所では、別のやり方で守ります。 その「別のやり方」を、ここで見ておきます。
🤔 RLSは特別な機能(🟢 基礎)
まず大事な前提から。RLS(行レベルセキュリティ)は、もともと PostgreSQL(ポストグレス)というデータベースの機能です。すべてのデータベースに付いているわけではありません。
- MySQL(マイエスキューエル)には、RLSがありません。
- 素の PostgreSQL でも、第4章でやったように 自分で明示的にONにしないと働きません。
🏤 たとえ話:建物によって設備が違う RLSは「各部屋に最初から付いている自動ロック」のようなもの。でも、そういう設備がない建物(MySQL)もあるし、付いていてもスイッチを入れ忘れている建物(素のPostgreSQL)もあります。
だから、世の中の多くの現場では RLS に頼らず、「アプリ側のコードで、取り出すたびに自分でしぼる」 という方式をとっています。次はその方式です。
🛠 RLSがない世界の守り方(🟢 → 🔧 実装)
やることは1つだけ。データを取り出す命令に、毎回かならず「ログイン中の人のものだけ」という条件を付けることです。
サーバー側のコードは、こんなSQLをデータベースに送ります。
-- ログイン中ユーザーのIDを $1 に渡す(プレースホルダ)
select * from tweets where user_id = $1 order by created_at desc;
1行ずつ読むと、こうです。
select * from tweets… 「tweets(ツイート)表から、全部の列をちょうだい」(*は「全部の列」)。where user_id = $1… ★ここが守りの本体。「user_id(持ち主)が$1と一致する行だけ」にしぼる、という条件。$1… プレースホルダ(差し込み口)。値を直接SQLの文字列に埋め込まず、あとから安全に渡す仕組み。これは後述のSQLインジェクション対策にもなります。order by created_at desc… 「created_at(投稿時刻)で 新しい順に並べて」(desc= 降順)。
ここで一番大事なのは、$1(ログイン中の人のID)を誰が決めるかです。答えは サーバー。サーバーはログイン状態(セッション)から「いまアクセスしているのは誰か」を知っているので、その人のIDを $1 に入れます。
⚠️ クライアント(ブラウザ)の言い分は信用しない。 「自分は3番さんです」とブラウザが送ってきても鵜呑みにしません。なりすませてしまうからです。持ち主の判定は、必ずサーバーが握る——これは第4章の
auth.uid()と同じ考え方です。
🔌 接続(コネクション)の一言:アプリはまずデータベースに「接続(コネクション)」をつないでから、こうした命令を送ります。Supabaseを使っていたときは、この配管(接続)も全部Supabaseが裏で用意してくれていました。
🔁 SupabaseのRLS ↔ 普通のDB 対応表
同じ「自分のだけ見せる」を、2つの世界はこう実現します。
| Supabase の RLS | 普通のDB(素のPostgreSQL / MySQL) | |
|---|---|---|
| 誰がしぼる? | データベースが自動で | アプリが毎回 手で(where を書く) |
| 付け忘れたら? | それでも守られる(DBが最後の砦) | そこから全部 漏れる 😱 |
| 向いている人 | 初心者・少人数(仕組みに任せたい) | 仕組みを自分で握りたい上級者 |
ひとことで言うと、RLSは「DBが守ってくれる」/普通のDBは「自分で守る」 です。便利さと引きかえに、普通のDBでは1か所の書き忘れが命取りになります。
🐘 PostgreSQL と 🐬 MySQL のざっくり違い
どちらも、世界中で使われている定番の リレーショナルDB(表=テーブルでデータを管理するデータベース)です。初心者のうちは「どっちも表でデータを保存する仲間」という理解で十分です。
- 🐘 PostgreSQL … RLSのような高機能な仕組みも持つ、多機能寄りのDB。Supabaseの中身はこのPostgreSQLです。
- 🐬 MySQL … とても広く普及していて、シンプル寄り。Webサービスの定番として長く使われてきました。
どちらが正解ということはなく、現場の事情で選ばれます。この教材でRLSが使えたのは、たまたま土台がPostgreSQLだったから、と覚えておくと混乱しません。
⚠️ ハマりどころ
whereの付け忘れ = 穴。 普通のDB方式では、where user_id = $1を書き忘れた瞬間、その命令は全員ぶんを返します。RLSと違い、DBは助けてくれません。- SQLを文字列でつなげない(インジェクション)。
"... where user_id = " + 入力値のように値を文字列連結すると、悪意ある入力で命令を乗っ取られます(SQLインジェクション)。必ずプレースホルダ($1など)を使うこと。 - 1か所の漏れが全体に波及する。 しぼる責任がアプリ側に散らばるので、どこか1本のSQLがミスっていると、そこだけ筒抜けになります。この「事故りやすさ」は第10章でも扱います。
📝 ことばメモ
- RLS(行レベルセキュリティ):DBが「行ごとに、触っていい人か」を自動でチェックする仕組み。PostgreSQL専用で、MySQLには無い。
- プレースホルダ:SQLに値を直接埋め込まず、
$1のような差し込み口であとから安全に渡す仕組み。インジェクション対策になる。 - SQLインジェクション:入力値を文字列連結でSQLに混ぜたとき、命令を乗っ取られる攻撃。プレースホルダで防ぐ。
- コネクション(接続):アプリがDBに命令を送るためにつなぐ通信路。Supabaseでは隠れていた配管。
- リレーショナルDB:表(テーブル)でデータを管理するタイプのデータベース。PostgreSQLもMySQLも仲間。
🔧 ちなみに、SQLを自分で書かずにデータベースを扱う「ORM」という道具もあります。これは付録Dで扱います。