ログインしたのに、ページを移動したら「もう一度ログインしてください」って表示されたことない?あるいは、アプリを使ってて「なんで毎回パスワード入れなくていいんだろう?」って思ったことない?実はその裏側には、ちゃんと「あなたが誰か」を証明する仕組みがあるんだ。その仕組みのひとつが JWT(ジェイダブリューティー) っていう技術。この記事を読めば、JWTがどんなものか、なんで便利なのか、しっかりわかるよ。
- JWTは デジタルの身分証明書 で、ログイン後に「あなたが誰か」を証明するために使われるよ
- データには 署名 がついているので、改ざんしてもすぐバレる仕組みになっているよ
- サーバー側でログイン情報を管理しなくていいので、大規模なシステム でも使いやすいのが特徴だよ
もうちょっと詳しく
JWTは「JSON Web Token(ジェイソン ウェブ トークン)」の略で、JSONというデータ形式を使ったトークン、つまり「認証用の文字列」のことだよ。JWTは3つのパーツ(ヘッダー・ペイロード・署名)をドット「.」でつないだ文字列として表現されるんだ。見た目は「xxxxx.yyyyy.zzzzz」みたいな感じで、一見なんのことかわからないけど、Base64という方式でエンコード、つまり変換されてるだけで、デコード(元に戻す)すれば中身を読めるよ。サーバーは受け取ったJWTの署名を検証することで、「このトークンは本物か?改ざんされてないか?」を確認できるんだ。大事なのは、JWTは誰でも中身を「読める」けど「書き換えられない」という点。署名があるから、改ざんはすぐバレるんだよ。
JWTは「見られてもOK、でも書き換えNG」な仕組み。パスワードなどの秘密情報はペイロードに入れないようにしよう!
⚠️ よくある勘違い
→ JWTはBase64でエンコードされてるだけで、暗号化ではないよ。デコードすれば誰でも中身を読めるんだ。
→ 署名によって「書き換え」は検知できるけど、「読む」のは誰でもできる。だからパスワードや個人情報はJWTに入れちゃダメ!
[toc]
JWTってそもそもなに?基本をおさえよう
「トークン」って何?
JWTを理解するには、まず「トークン」という言葉を知っておく必要があるよ。トークンとは、つまり「本人確認のための合言葉のような文字列」のことだ。
イメージしてみてほしいんだけど、映画館に入るときのチケットってあるじゃない?あのチケットがあれば「この人は正規にお金を払って入る権利がある人だ」って証明できるよね。JWTはまさにデジタル版のチケットなんだ。ログインに成功したら「はい、これがあなたのチケットです」とサーバーからJWTをもらって、次からはそのJWTを見せるだけで「この人はログイン済みだ」ってわかるようになるんだよ。
JWTの正式名称と読み方
JWTは「JSON Web Token」の略で、「ジェイダブリューティー」または「ジョット」と読むこともあるよ。JSONはつまり「キーと値のペアでデータを表すデータ形式」のことで、たとえば {"name": "田中", "role": "admin"} みたいな書き方のことだ。このJSONを使ってWebの認証(つまり「あなたが誰か確認する仕組み」)に使えるようにしたのがJWTってわけだよ。
JWTは2015年にRFC 7519として標準化されていて、今では世界中のWebサービスやアプリで広く使われてる技術なんだ。GitHubとかGoogleとかもJWTを使ってるよ。
JWTの3つのパーツを解剖しよう
ヘッダー(Header)
JWTは「ヘッダー」「ペイロード」「署名」の3つのパーツからできてて、それぞれをドット「.」でつないだ文字列になってるよ。たとえば実際のJWTはこんな見た目だ:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IuWPjOS6jiIsInJvbGUiOiJ1c2VyIn0.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
なんか暗号みたいだよね。でも怖くないよ。一番最初のパーツ(ドットの前)がヘッダーで、Base64でデコードするとこんな感じになるよ:
{"alg": "HS256", "typ": "JWT"}
これはつまり「このJWTはHS256という署名アルゴリズムを使ってますよ」という説明書みたいなものだ。アルゴリズムとは、つまり「どんな計算方法でハンコを押したか」ということだよ。
ペイロード(Payload)
2番目のパーツがペイロードで、ここに実際の情報が入ってるんだ。「ペイロード」とはつまり「荷物の中身」という意味で、トークンに乗せたいデータを格納する部分だよ。デコードすると:
{"sub": "1234567890", "name": "田中", "role": "user", "exp": 1716239022}
こんな感じで、ユーザーのID・名前・権限・有効期限(expはexpirationの略で「いつまで有効か」のこと)などが入ってるよ。ここは誰でも読めるから、さっきも言ったとおりパスワードや個人情報を入れてはダメだよ。
署名(Signature)
3番目のパーツが署名で、これがJWTの「安全性の核心」なんだ。署名はサーバーだけが知ってる「秘密鍵」を使って作られるハッシュ値、つまり「改ざん検知用の特殊なチェックサム」だよ。
ハッシュとはつまり「同じデータからは必ず同じ値が生成されるけど、ちょっとでもデータが変わると全然違う値になる」という特徴を持つ計算のことだよ。だから、ペイロードを書き換えても署名が変わらなければ「あれ、データと署名が合わない、偽物だ!」とバレてしまう仕組みなんだ。これは封筒に封蝋(シール)を押すようなイメージで、一度開けると痕跡が残って「誰かに開けられた」とわかるのと同じだよ。
JWTを使ったログインの流れを追いかけよう
ステップ1:ログインリクエスト
まず、ユーザーがIDとパスワードを入力してログインボタンを押すと、その情報がサーバーに送られるよ。ここまでは普通のログインと同じだね。
ステップ2:サーバーがJWTを発行する
サーバーはIDとパスワードをチェックして、正しければJWTを作って返してくれるよ。このとき、ペイロードに「このユーザーはAさんで、権限はuserで、有効期限は1時間後」みたいな情報を入れてから、秘密鍵で署名して完成!これがユーザーに渡されるチケットになるんだ。
ステップ3:クライアントがJWTを保存する
もらったJWTをブラウザかアプリが保存しておくよ。よく使われるのは「ローカルストレージ(ブラウザ内のメモ帳みたいな場所)」か「Cookie(ブラウザに保存される小さなデータ)」だ。どちらにも一長一短があって、セキュリティの観点から議論されることも多いテーマなんだよ。
ステップ4:次のリクエストからJWTを送る
次にサーバーにアクセスするとき、HTTPリクエストの「Authorizationヘッダー」にJWTを入れて送るよ。ヘッダーとはつまり「リクエストに添付するメタ情報(データについての説明書き)」のことだ。こんな感じで送るよ:
Authorization: Bearer eyJhbGciOiJIUzI1NiIs...
「Bearer」はつまり「これがトークンです」という宣言みたいなものだね。
ステップ5:サーバーが署名を検証する
サーバーはJWTを受け取ると、署名が正しいか確認して、有効期限が切れてないか確認して、OKなら「はい、この人はAさんです」とわかって処理を続けてくれるんだ。このとき、サーバーはデータベースを見に行かなくてもいいから、とっても高速に処理できるのも魅力だよ。
JWTのメリットとデメリットをセットで理解しよう
JWTの3つのメリット
JWTには大きく3つのメリットがあるよ。
- サーバーがセッション情報を持たなくていい:昔ながらのセッション方式(つまりサーバー側で「誰がログイン中か」のリストを管理する方法)と違って、JWTはトークン自体に情報が入ってるから、サーバーはJWTを受け取るだけで誰かわかるんだ。これを「ステートレス(状態を持たない)」と言って、サーバーを増やしても同じJWTでどのサーバーでも認証できるよ。
- 複数のサービスで使い回しやすい:GoogleのJWTをYouTubeでも使えるように、同じJWTを複数のサービスで使えるんだ。これをシングルサインオン(SSO)と言って、つまり「一回ログインしたら複数のサービスに自動でログインできる仕組み」のことだよ。
- モバイルアプリとの相性が良い:スマホアプリはCookieが使いにくいけど、JWTならHTTPヘッダーに入れて送れるから、アプリの認証にとても向いてるんだ。
JWTの3つのデメリット
一方で、デメリットもあるから知っておこう。
- トークンの無効化が難しい:一度発行したJWTは、有効期限が来るまで使えてしまう。もし「このユーザーをすぐにログアウトさせたい」という状況が起きても、JWTは有効期限までずっと使えてしまうんだ。これを解決するために「ブラックリスト」を持ったりするけど、そうするとステートレスの良さが薄れてしまうというジレンマがあるよ。
- サイズが大きくなりやすい:JWTはペイロードに情報を入れすぎると、毎回のリクエストで大きなデータを送ることになるよ。特にモバイル回線では通信コストが上がる可能性があるんだ。
- 盗まれたら悪用されるリスク:JWTが盗まれると、有効期限が切れるまで攻撃者がなりすましできてしまう。だから保存場所やHTTPS(通信の暗号化)をしっかり設定することがとても大切だよ。
JWTを安全に使うための注意点
有効期限は短めに設定しよう
JWTの有効期限は短めに設定するのがセキュリティの基本だよ。よくある設定は「アクセストークンは15分〜1時間、リフレッシュトークンは7〜30日」というパターン。アクセストークンとはつまり「実際の操作に使う短命なトークン」で、リフレッシュトークンとはつまり「アクセストークンを更新するための長命なトークン」のことだよ。
牛乳で例えると、アクセストークンはコンビニで買って今日使うための小さいパック(すぐ使い切る)で、リフレッシュトークンは冷蔵庫のストック(長く保管して必要なときに新しいパックと交換)みたいなイメージだよ。
必ずHTTPSを使おう
JWTをHTTPSなしで送ると、通信を傍受されてトークンを盗まれる可能性があるよ。HTTPSとはつまり「通信内容を暗号化して、第三者に盗み見られないようにする通信規格」のことだ。今どきのWebサービスはほぼ全部HTTPSを使ってるけど、自分でサービスを作るときは必ず設定しようね。
アルゴリズムの指定に注意しよう
JWTのヘッダーには署名アルゴリズムを指定するんだけど、「none」(署名なし)という値を受け入れてしまうと、攻撃者が署名なしのJWTを作って偽装できてしまうよ。これは実際に起きた有名な脆弱性で、つまり「セキュリティ上の穴」だよ。だからサーバー側で「このアルゴリズム以外は受け付けない」と明示的に指定することがとても大切なんだ。
センシティブな情報はペイロードに入れない
さっきも言ったけど、JWTのペイロードはBase64でデコードすれば誰でも読めるよ。だからパスワード・クレジットカード番号・マイナンバーなどの個人情報や秘密情報は絶対にペイロードに入れないようにしよう。JWTに入れていいのは「このユーザーのIDは何番で権限は何か」程度の最低限の情報だけにするのがベストプラクティス(つまり「みんなが認める一番良いやり方」)だよ。
