🔒

ボーッと生きてんじゃねえよ!!!JWTってどうやって認証・認可してるの〜?

2024/12/15に公開

5 歳児「ねぇ、jwt で認証・認可を行うアプリケーションが増えてるみたいだけど、どういう仕組みで web サービスは JWT で認証してるの〜〜??」
僕「んー。なんか不思議な力で???・・・🤔」
5 歳児「ボーッと生きてんじゃねえよ!!!!😡😡😡💨」

ということで、この記事では jwt とは?という話から始め、web サービスがどのように jwt で認証を行なっているのかについて説明していこうと思います!

本記事は、クロスマートアドベントカレンダー 2024 の 15 日目の記事として執筆しています!
https://qiita.com/advent-calendar/2024/xmart

想定読者

  • jwt 認証聞いたことあるけどよくわからない
  • jwt 認証を普段から利用してるけど、何気なく使っている

以上の方々を想定して執筆しています!jwt 認証についてなんとなくわかってもらえると幸いです!

Token を用いた web API の認証・認可

web サービスでユーザーの情報を扱う場合には、session id を用いる方法が一般的でした。

session idベースの認証方法
session id を使用した認証フロー

この session id を用いる方法は、バックエンドのサーバーで次のことをする必要があります。

  • 各リクエストに含まれている session id が正しい id なのかを判別する
  • session id からリクエストしてきたユーザー照合する

この特徴から、多くのリクエストが発生した場合にユーザーの認証にかかる負荷が増大することや、ユーザーごとの権限管理などの複雑なユースケースに対応するための実装コストが高いという問題があります。

これらの課題を背景に、token ベースの認証が注目されるようになりました。

token ベースの認証には、「token に userId や token の有効期限などの 任意の情報を含めることができる」特徴があります。

alt text
token を使用した認証方法フロー

この方法を用いることで、リクエストの認証を処理する際に、DB などへユーザー情報の取得などを行わなくても良くなります。そのため、DB の負荷を下げることができたり、権限管理などの複雑なユースケースへの対応が簡単になるなどの利点があります。

ところで、ユーザーに返す情報である token に、ユーザー情報などを含めてもいいのでしょうか?
例えば、悪意のあるユーザーが、token 内部に含まれている userId を書き換えることにより、他のユーザーになりすまして API にアクセスすることができるのではないでしょうか。

現在広く用いられている json web token という token では、そのような悪事を防ぐ仕組みが備わっています。

jwt とは?

jwt とは、json web token の略で、「じょっと」と一般的に発音されています。
jwt は、json を改ざんされずに json をやりとりする仕組みです。この、「改ざんされないで安全に json をやりとりすることができる仕組み」を認証に応用したものが、jwt 認証になります。
まず初めに、jwt がどのように安全に json をやりとりすることを保証しているのかについて説明していきます。

jwt の構成

ここに、例として私が適当に作った jwt を示します

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiJ0ZXN0IHVzZXIgaWQiLCJpYXQiOjE3MzQwMTY2NjEsImV4cCI6MTczNDYyMTQ2MX0.dX-vieKT_2px5MQW75Ik3Zx7VMBWCiHOneTCKWsRiR0

この jwt はよく見ると二つの"."があります。この"."を挟んで 3 つの部分に分かれているそれぞれが、ヘッダ、ペイロード、署名と呼ばれています。

ヘッダ    → eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
ペイロード → eyJ1c2VySWQiOiJ0ZXN0IHVzZXIgaWQiLCJpYXQiOjE3MzQwMTY2NjEsImV4cCI6MTczNDYyMTQ2MX0
署名      → dX-vieKT_2px5MQW75Ik3Zx7VMBWCiHOneTCKWsRiR0

それぞれのセクションについてみていきます。

ヘッダ

先ほどの文字列のヘッダ部分を抜き出したものです

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

この部分は、この jwt 自体の情報を Base64 でエンコードしたものになります。(厳密には、Base64 ではなく Base64 を URL safe にしたBase64 URL safeエンコードです)
実際にこの部分を Base64 でデコードしてみると以下の json を確認できます。

$ echo eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9 | base64 -d
>>> {"alg":"HS256","typ":"JWT"}

alg と、typ のキーからなる json を得ることができました。

この情報から、jwt に用いている暗号アルゴリズムが HS256 であることと、このトークン自体が JWT であることがわかります。

ペイロード

中間部分を抜き出したものが以下になります

eyJ1c2VySWQiOiJ0ZXN0IHVzZXIgaWQiLCJpYXQiOjE3MzQwMTY2NjEsImV4cCI6MTczNDYyMTQ2MX0

ここの部分には、user に関する情報など、サーバー側で作られた json を Base64 エンコードした文字列になっています。
実際にデコードしてみると以下のような json を確認できます。

$ echo -n eyJ1c2VySWQiOiJ0ZXN0IHVzZXIgaWQiLCJpYXQiOjE3MzQwMTQ3NTMsImV4cCI6MTczNDYxOTU1M30 | base64 -d
>>> {"userId":"test user id","iat":1734014753,"exp":173461955

ここにはサーバー側で任意の json を含めることができます。例えば、認証・認可の用途であれば、今回の UserId の他にも、その token の発行された Unix time や有効期限、ユーザーの権限の情報などが含まれていることが一般的です。

ヘッダ、ペイロードの情報は、サーバーからフロントに送られてくるものです。そのため、上で示したように、base64 でデコードすればクライアント側で簡単に情報を確認することができます!
権限などの情報を token に含めておけば、権限ごとの画面の出しわけなども、バックエンドへの問い合わせなしで行うことができるかもしれませんね。

署名

ヘッダー、ペイロードの情報が、単なる json を Base64 エンコードしたものだとわかりましたが、これを改ざんできなくするための仕組みが、末尾についている署名です。
詳しくは次の章で説明します。

署名がどのように改ざんを難しくしているか

署名が、ヘッダー、ペイロードが改ざんされないようにする仕組みを理解するために、「署名をどのように作るのか」と「token をどのように判別するのか」について説明します。

署名の作り方

以下にサーバー側で署名がどのように作られているのかの図を示します。

alt text
jwt を作成するフロー(HS256 を用いている例)

  1. まず、ヘッダーになる json, ペイロードになる json を Base64 エンコードし、"."で結合した文字列を作成します
  2. その後、サーバー側で保持している秘密の文字列(秘密鍵)と、1 で作成した文字列をを用いて hash 化します。
  3. hash 化して得られた値を Base64 エンコードします(署名)
  4. ヘッダー、ペイロード、署名を"."で結合して token を作成します。

ハッシュ化については、こちらのサイトでわかりやすく解説されているため、そちらをご参照ください。
https://www.ntt-west.co.jp/business/glossary/words-00172.html

このような手順を以て、token に情報を含めています。

token の判別方法

この Token が正しい Token であるかを判別するには、以下の方法で行います。

alt text
正しい jwt token かどうかの判別方法(HS256 を用いている例)

  1. token のヘッダー、ペイロード部分を、jwt を作成した時と同様に hash 化する
  2. hash 化されたヘッダーとペイロードが、後ろの署名と一致していた場合、正しいトークンだとわかる。

例えば、ここでペイロードの中の userId が変更されていた場合は、リクエストされたヘッダーとペイロードを hash 化した際の署名が、token に含まれる署名と一致しません。
このことから、token が何者かによって改竄されていることを検知することができます。

alt text
正しくない jwt token がリクエストされた時の例

また、正しい署名を得るためにはサーバー側で保持している秘密鍵が必要です。そのため、サーバー側で厳重に管理されている秘密鍵を盗まない限りは、クライアント側で正しい署名を作ることもできません。
安全ですね!

おわりに

このように、jwt を用いることで、token にユーザーの情報などを含めた上で、情報の改ざんを防ぐことができる token を作ることができます。
そんな利便性と安全性を兼ね備えた jwt を用いたサービスが増えているのも納得です。

注意点ですが、今回の例は、署名を作成する方法として、対称鍵暗号方式の HS256 による署名アルゴリズムを用いておりました。
Auth0 などの認証サービスが使用している Open Id Connect (OIDC) も、jwt の仕組みを用いています。しかしそちらは RS256 という暗号方式を用いており、今回説明したフローとは多少異なります。

次回の記事では、今回開設したことを踏まえて、実際に簡単なエンドポイントに認証機能をつけてみたいと思います!

そして 16 日目の記事もお楽しみに!!!
https://qiita.com/advent-calendar/2024/xmart

参考にさせていただいた記事

https://www.ntt-west.co.jp/business/glossary/words-00172.html

https://zenn.dev/mfykmn/articles/eeaeb9a05130b8

https://www.ios-net.co.jp/blog/20231115-1720/

GitHubで編集を提案

Discussion