Q1サーバーが利用者のパスワードを保存するとき、データベースに置かれているものはどれですか。
ログイン機能の仕組み — 認証・セッション・Cookie
この記事は、プログラミングやバイブコーディングをする上で最低限身につけたほうがよい IT の実践的な知識を 1 から身につける「IT 基礎知識講座の一部」です。
ログイン機能は、本人かを確かめる認証と、そのあとも同じ人と分かるセッションでできています。ハッシュと Cookie の役割を図解で確かめます。
この記事は、ログイン機能の仕組みを扱います。
ログイン機能は、本人かを確かめる仕組みと、そのあとも同じ人だと分かる仕組みの 2 つでできています。
「ハッシュ化」「セッション方式」「JWT」は、1 つのログイン機能を場面ごとに呼び分けたものです。
上の枝が最初の 1 回だけ行う確認、下の枝がそのあと毎回行う確認です。
認証は本人かを確かめること、認可は何をしてよいかを決めること
認証(authentication。利用者が本人であることを確かめること)は、ログインのときに行う判断です。
認可(authorization。確かめた利用者に、何をしてよいかを決めること)は、そのあとに行われる別の判断です。
- もとにするもの — ID とパスワード、ログイン後は Cookie のセッション ID
- 通らないときの応答 — ログイン画面に戻す
- パスワードで確かめるのは 1 回、そのあとはセッション ID
- もとにするもの — 予約の持ち主が誰か、利用者が管理者か
- 通らないときの応答 — 権限がありませんと返す
- 操作するたびに、毎回確かめる
同じ tanaka さんの操作でも、2 つは別々に判定されます。
認証で止まる行と、認証を通ったあとに認可で止まる行があります。
設定の「管理者だけが使える」「自分のデータしか見えない」は、この認可の話です。
本人かを確かめるのが認証、してよいかを決めるのが認可
ログインできることと、その操作をしてよいことは、別々に決まります。
本人かどうかを確かめるのが認証、その人がその操作をしてよいかを決めるのが認可で、認可は操作するたびにサーバー側で確かめます。
パスワードはハッシュにして保存し、ハッシュ同士を比べる
認証でまず押さえるのは、サーバーがパスワードそのものを保存していないことです。
保存されているのはハッシュ(hash。元の文字列から決まった手順で作った、元には戻せない文字列)です。
| 打ち込んだ文字 | その場でハッシュ化 | 保存済みのハッシュと比べる | 判定 |
|---|---|---|---|
| hanabi2026(正しい) | a3f9...c1 | a3f9...c1 と同じ | ログイン成功 |
| hanabi2025(1 文字違い) | 7b20...e4 | a3f9...c1 と違う | ログイン失敗 |
| Hanabi2026(大文字違い) | d15c...8a | a3f9...c1 と違う | ログイン失敗 |
1 文字違うだけで、できあがるハッシュはまったく別のものになります。
元に戻せなくても確かめられるのは、比べる相手もハッシュだからです。
サーバーが持っているのは、戻せない文字列だけです。
- email — tanaka@example.com
- password_hash — a3f9...c1
- created_at — 会員登録した日時
- hanabi2026 という打ち込んだ文字
- 打ち込んだ文字に戻せる形のデータ
- 今ログイン中かどうかの状態
ハッシュで保存するのは、データベースの中身が漏れたときに備えるためです。
よく使われる文字列は当てられるので、ハッシュ化の前に利用者ごとに違う文字列(ソルト)を足します。
送るときの HTTPS は途中で読まれないための対策で、保存の形とは別の話です。
保存してあるのはハッシュで、比べる相手もハッシュ
サーバーは、利用者が打ち込んだパスワードを持っていません。
持っているのは元には戻せない文字列だけで、ログインのたびに打ち込まれた文字を同じ手順にかけ、できた文字列が保存してあるものと同じかどうかを見ています。
ログインしたあとは、Cookie のセッション ID でサーバーが同じ人と分かる
ログインに成功しても、次のページを開くリクエストは、別のリクエストです。
HTTP はステートレス(1 回の処理が、前の処理を覚えていないこと)なので、直前に誰がログインしたかをサーバーは持っていません。
ログインが通ったことをサーバー側に記録し、その記録を指す文字列をブラウザが預かります。
サーバー側に残すこの記録がセッション(session)、それを指す文字列がセッション ID(session ID)、その文字列をブラウザに預けて毎回送らせる仕組みがCookie(cookie)です。
- Cookie: session_id=3f9a1c...e81b
- 同じサイトへのリクエストに毎回自動で付く
- この文字列のほかは何も持っていない
- セッションの記録 3f9a1c...e81b は tanaka さん
- この記録に有効期限が付いている
- ログアウトすると、この記録を消す
ブラウザ側にあるのは 3f9a1c...e81b という文字列だけで、名前も権限も入っていません。
誰のログインかが分かるのは、サーバー側の記録と結び付いたときだけです。
上の行だけが記録と結び付くので、パスワードを聞き直さずに予約一覧が返ります。
この受け渡しは、次の 2 行の形で行われています。
# ログインに成功したとき、サーバーが返す行
Set-Cookie: session_id=3f9a1c...e81b; HttpOnly; Secure
# 以後、ブラウザが同じサイトへ送るリクエストに毎回付く行
Cookie: session_id=3f9a1c...e81b
Set-Cookie の後ろの 2 つは、ブラウザへの指示です。
HttpOnly は画面の JavaScript からこの Cookie を読めなくし、Secure は HTTPS のときだけ送らせます。
Cookie の中身は利用者が書き換えられるので、そのまま信じるかどうかで結果が変わります。
Cookie はブラウザに保存されるので、利用者が中身を書き換えて送れます。
Cookie に入れるのはセッション ID だけにし、判断はサーバー側の記録で行います。
左の囲みに置かれるのは、意味を持たない文字列 1 つだけです。
誰であるか、何をしてよいかを決める材料は、すべて右の囲みの中にあります。
ブラウザが預かるのは、記録を指す文字列 1 つだけ
サーバーは、リクエストが届いた時点では、それが誰からのものか分かりません。
分かるのはログインしたときに作った記録とブラウザが毎回送る文字列が結び付いているからで、記録が消えるか期限が切れれば、同じ文字列を送ってもログイン画面に戻ります。
セッション方式と JWT の違い — 記録をサーバーが持つか、文字列が持つか
「セッション方式か JWT か」は、ログイン済みの証明をどちらが持つかの選び方です。
もう 1 つは、サーバーに記録を残さず、証明そのものをブラウザに持たせる方式です。
毎回付けて送るこの証明の文字列がトークン(token)で、JWT(JSON Web Token。利用者の ID と有効期限と署名をまとめた文字列)が代表的な形式です。
- tanaka さんの利用者 ID
- 有効期限(この時刻まで使える)
- 中身から計算した文字列
- 中身を書き換えると計算が合わなくなる
署名があるので、サーバーは記録と照合しなくても、届いた文字列が自分の発行したものだと分かります。
| 観点 | セッション方式 | JWT |
|---|---|---|
| ログイン成功時に渡すもの | session_id=3f9a1c...e81b | eyJhbGci... ID と期限と署名 |
| 判断のもとがある場所 | サーバーの中の記録 | 文字列の中の中身 |
| 毎回の確かめ方 | 記録と照合する | 署名を検証する |
JWT は中身を持っているので、サーバーの記録がなくても受け入れられます。
セッション方式は記録を消せば、次のリクエストから通らなくなります。
JWT は期限が来るまで通るので、すぐ止めたいときは無効にしたものの一覧を別に持ちます。
この記事では、セッション方式は Cookie、JWT は文字列として分けて描きましたが、実際には JWT を Cookie に入れる構成もあります。
置き場所が変わっても、記録と照合するか署名を確かめるかという違いは同じです。
記録をサーバーが持つか、文字列が中身ごと持つか
どちらの方式でも、ブラウザが毎回 1 つの文字列を付けて送るところは同じです。
違うのは受け取ったサーバーが何をするかで、セッション方式は自分の記録を見に行き、JWT は文字列に付いた署名を確かめます。
理解度チェック
まずは1問ずつ答えてみましょう。
Q2ログイン後に別のページを開いたとき、サーバーが同じ利用者だと分かるのはなぜですか。
Q3ログイン済みの利用者が他人の予約を削除しようとして、サーバーに断られました。この判断はどれですか。