Webにおける認証情報、JWTの取り扱いとか
概要
認証情報をどこで持つ(localStorageやCookie)とか、セッションでやるとかとか、今一度整理してみる。
要件
- スケールアウトが簡単である。ステートレスが望ましい
- 一回ログインしたら後は基本永続にしたい
- サーバー都合でログアウトさせられること。不正利用確認できたら止められる
- 将来的にアプリでも使えるAPI
書き出してみたら意外と少ない
用語の定義
当記事では以下の用語は以下とする。
-
ステートレスな認証
: 認証情報をサーバー側で管理しません。ここではJWTを使用。 -
ステートフルな認証
: 認証情報をサーバー側で管理します。DBやRedisなどで管理。 -
アクセストークン
: ステートレスな認証として使用されます。ここではJWTを指す。 -
リフレッシュトークン
: アクセストークンを発行するためのトークン。ステートフルな認証として使用されます。DBやRedisで管理。 -
リボーク
: トークンをサーバー側の都合で無効化すること
JWTとは
-
{"user_id":1,"有効期限":"2024-01-01"}
というJSONがあります。 - JSONをサーバーだけが知ってるキー値(例:
ABCDEF
)を使ってハッシュ化(例:HOGEHOGE
)します。 - クライアントには
JSON+ハッシュ値
を渡します。(例:{"user_id":1,"有効期限":"2024-01-01"}HOGEHOGE
)
これがJWTです。
サーバーサイドはJSON+ハッシュ値
を受け取り、検証(JSONをハッシュ化したらHOGEHOGEになるか)することでJSONが改竄されてないことを確認できます。
つまり同じハッシュ値を持ってるだけでどのサーバーでも検証が出来るってことです。
要件を満たす
スケールアウトが簡単である。ステートレスが望ましい
アクセストークン(JWT等)を使用することで実現が可能。
ただし仕組み上サーバーサイドでリボークが出来ない為、有効期限は短く設定しておくこと。
(厳密にはリボークできるが、サーバーのキー値を変える為影響が大きい)
一回ログインしたら後は基本永続にしたい
リフレッシュトークンの期限を無限(または長期間)とすることで対応。
サーバー都合でログアウトさせられること。不正利用確認できたら止められる
リフレッシュトークンはステートフルな認証(DBやRedis管理)である為、データを消すことでアクセストークンの新規発行を止めることが出来ます。
アクセストークンは期間内であれば検証が通るので、有効期間内の悪用を許容する必要があります。
重要な情報に対するアクセスにはパスワードなど制限をかけるといった対策をしましょう。
将来的にアプリでも使えるAPI
ログインした際、SetCookieだけではなくレスポンスボディにリフレッシュトークンを含めてやると使いやすいかも。アプリからSetCookieの値取れると思うのでマストではない
まとめ
アクセストークン(ステートレスな認証)について
ここで話しているアクセストークンはステートレスなものでDBやRedisで検証が行われないものを指す。
その手段として採用されているのがJWTという話。
→流出した場合キー値の変更以外で止められないので、期限は短めに設定しておく
リフレッシュトークン(ステートフルな認証)について
アクセストークンの再発行に使用するトークン。
リフレッシュトークン自体はDBやRedisに格納する為、リボークが可能。
仕組み上リフレッシュトークンについてはステートフルにするしかない。
許容するリスクと対策
アクセストークンが流出した場合、有効期限内の悪用は許容する。
リフレッシュトークンをリボークすることで、永続的な悪用はさせない。
また、パスワードの変更や決済といった大切な動作を行う場合はパスワードの入力を求めるなどで対策とする
Webサイトにおけるトークン情報の格納先
アクセストークン
: 正直どこでもいい。localStorageでもいいし、SPAならインメモリでもいい。
リフレッシュトークン
: httpOnlyなCookieとして格納がベター。localStorageでもいいけど
どこでもいい&httpOnlyなCookieとしての格納がベターな理由
XSSを行われた際に直接トークン取られるか、取れないかの違いでしかない。
httpOnlyなCookieだと直接値を取られることはないが、withCredentials
を付けてPOSTすることでアクセストークンの再発行が可能なので、XSSには効果が薄い。
サイト閉じたら不正利用されなくなるので、そういった意味では後者がベターという話。
結論
ステートフルな認証(DBやRedis)とステートレスな認証(JWT)を使いこなすことで認証のオーバーヘッドを軽減することができる。
JWTはあくまでステートレスに個人を特定&検証するための仕組みである。