認証/認可まわりの仕組みや一般的なスキームや用語、なんだかややこしい!のでまとめておきたい!と思い書きます。
そもそも
認証(Authentication)
本人であるかを確認すること
認可(Authorization)
認証された人が何をしていいかを確認すること
JWT(ジョットと読む)
他者間で情報をJSONの形で安全にやり取りするための、コンパクトで自己完結的な方法を定義したもの。デジタル署名がされているので JWTに含まれている情報は信頼できる(もし改ざんされていたら検知できる。改ざんを防ぐものではない)。JWTに含まれている情報には誰でもアクセスできるので、機密情報は基本的にJWTに含むべきではない。
▼ 構造
ヘッダー、ペイロード、シグネチャが、xxxxx.yyyyy.zzzzzのように.で繋がった形をしている。
- ヘッダー:たいてい、トークンのタイプとシグネチャのアルゴリズムを含んでいる。Base64Urlの形でエンコードされ、 JWTの最初のパートを構成する
例 { "alg": "HS256", "typ": "JWT" } - ペイロード:ユーザーやその他の情報を含む部分。各情報はクレームと呼ばれる。Base64Urlの形でエンコードされ、 JWTの真ん中のパートを構成する
例 { "sub": "1234567890", <- クレーム "name": "John Doe", <- クレーム "admin": true <- クレーム } - シグネチャ:ヘッダー、ペイロード、署名のための鍵、ヘッダーで指定されているアルゴリズムを使って署名されることによって生成される部分。 JWTの最後のパートを構成する。 JWTに含まれている情報が改ざんされていないことを証明する
JWTはJWT Debuggerでデコードしたり生成したりできる。
Cookie
サーバーがユーザーのウェブブラウザに送信する小さなデータ。ブラウザはCookieを保存したり、新しく作成したり、既存のCookieを変更したり、後でリクエストされた時に同じサーバーへそれらを送り返したりすることができる。
使用用途は主に3つ:
- セッション管理: ユーザーのログイン状態等のサーバーが覚えておくべきもの
- パーソナライズ: 表示言語等のユーザー設定
- トラッキング: ユーザーの行動の記録と分析
Cookieはデフォルトでは全てのエンドユーザーがアクセス・変更可能。安全性を高めるためには以下のような方法がある。
- Cookieへのアクセスをブロックする
-
Secure属性をつける。HTTPSプロトコル上の暗号化されたリクエストでのみサーバーに送信されるようになる -
HttpOnly属性をつける。JavaScriptのdocument.cookie APIではアクセスできないようになる。これはクロスサイトスクリプティング(XSS)攻撃の緩和に役立つ
-
- Cookieの送信先を定義する
-
Domain属性を活用する。そのCookieを受信できるサーバーを指定することになる。ただし、この設定がない場合はそのCookieを設定したサーバーでしか受信できない(サブドメインでも受信できない)ことにデフォルトでなっているが、これを設定することによってサブドメインも含めてそのドメインで受信できるようになる=設定すると制限を緩めることになる -
Path属性を活用する。Cookieヘッダーを送信するためにリクエストされたURLが含んでいるべきパスを設定することになる -
SameSite属性を設定する。クロスサイトリクエストフォージェリー(CSRF)攻撃の緩和に役立つ。Strict,Lax,Noneのうちどれかの値を設定できる。デフォルト値はLax
-
XSS攻撃(Cross-Site Scripting)
Webアプリにスクリプトを埋め込むことが可能な脆弱性がある場合に、これを悪用して不正なスクリプトを埋め込み、ユーザーのブラウザ上で実行する攻撃。この攻撃の影響はwebアプリではなくそのページを閲覧したユーザーに及ぶ(例:Cookieの漏洩)。
CSRF攻撃(Cross-Site Request Forgeries)
ログイン機能を有するwebアプリにおいて、ユーザーからのリクエストが本当にそのログインしたユーザーが意図したものかどうかを識別する仕組みを持たない場合に、外部サイトを経由した悪意のあるリクエストを受け入れさせる攻撃。ユーザーが意図しない送金や退会処理等が発生する。
セッション
Webサイトやアプリ側がユーザーを識別するために利用される仕組み。認証に成功した後のユーザーの一連の行動のことを指したりもする。認証された状態やユーザーの状態をwebアプリ内で保存しておくために、webアプリはセッションIDまたはトークンを発行する。発行されたものはたいていCookieに保存される。Cookieに認証情報やユーザーの状態を直接記録することもできるが、Cookieは誰でもアクセス可能なので、セッションIDまたはトークンだけを保存するようにする(これがセッションベース認証)。
認証のストラテジー
セッションベース認証(Cookieベース認証とも)
サーバーからクライアントに渡されるものはセッションIDと呼ばれるIDのみで、ユーザーの情報自体はサーバー側のDB等で管理されているという、ステートフルな認証方法。認証/認可の流れは以下。
- 【認証】 パスワード等でユーザーを認証した後、サーバーはセッションIDを発行する。この時、サーバーはセッションIDとユーザー情報を紐付けて、サーバーのDB等に保存しておく
- サーバーはHTTPレスポンスのヘッダーを使ってクライアント(ブラウザ)にセッションIDを返す
HTTP 200 OK Set-Cookie: <sessionId> - クライアント(ブラウザ)はCookieにセッションIDを保存する
- 【認可】 クライアントからサーバーにリクエストを送る時、クライアント(ブラウザ)はCookieに保存しておいたセッションIDをHTTPリクエストのヘッダーに自動的に含めて送る
Cookie: <sessionId> - リクエストを受け取ったサーバーは含まれているセッションIDを確認し、対応するユーザー情報を取得する
- サーバーはサーバー側の実装で定義されていることに従いながら、そのユーザーにアクセス可能なリソースをクライアントに返す
- ログアウトすると、クライアント(ブラウザ)でセッションIDを別の値に更新する+サーバーでセッションIDとユーザー情報の紐付けを破棄する。これで元のセッションIDを使って認証/認可することはできなくなる
図解
▼ Pros
- セッションIDを無効化できる。例えば、ログアウトしたらそのセッションIDは無効になるので、万一そのセッションIDが盗まれていても悪用されにくい。管理者側が強制的にセッションを無効にすることも可能
▼ Cons
- ユーザーが増加したときのサーバー負荷がトークンベース認証と比べて大きいため、スケールしにくい
トークンベース認証
サーバーからクライアントに渡されるものはアクセストークンと呼ばれる基本的にはJWT形式のトークンで、ユーザーの情報はそのトークンに保存されている(サーバー側では管理していない)という、ステートレスな認証方法。認証/認可の流れは以下。
- 【認証】 パスワード等でユーザーを認証した後、サーバーはアクセストークンを発行する。このアクセストークンはユーザー情報と紐づけられてはいない。このアクセストークン自体がユーザー情報を持っている(有効期限やそのトークンでアクセス可能なリソースの範囲等)。アクセストークンにはJWTが使われることが多い
- サーバーはクライアント(ブラウザ)にアクセストークンを返す
HTTP 200 OK { token: <token> } - クライアント(ブラウザ)はアクセストークンを保存する。保存場所の選択肢は以下:
- Cookie: 一般的な選択肢。XSS、CSRFに弱い。
HttpOnly属性をつけるとXSS攻撃のリスクは緩和できる - LocalStorage: XSSに弱い。意図的に削除しない限り永久に有効なので取り扱いに注意が必要
- SessionStorage: XSSに弱い
- Cookie: 一般的な選択肢。XSS、CSRFに弱い。
- 【認可】 クライアントからサーバーにリクエストを送る時、クライアント(ブラウザ)は保存しておいたアクセストークンを基本的にはHTTPリクエストのヘッダー、AuthorizationのBearerスキーマを使って送る
Authorization: Bearer <token> - リクエストを受け取ったサーバーは含まれているアクセストークンの署名を検証。改ざんされていないか、有効期限等も問題ないかを確認する。その後、アクセストークンが持っているペイロードを読み取り、ユーザー情報を取得する
- サーバーはサーバー側の実装で定義されていることに従いながら、そのユーザーにアクセス可能なリソースをクライアントに返す
- ログアウトすると、クライアント(ブラウザ)は保存していたアクセストークンを破棄する
図解
▼ Pros
- ユーザーが増加したときのサーバー負荷がセッションベース認証に比べて少なく、よりスケーラブル
▼ Cons
- トークンの無効化がしづらい。例えば、ログアウトしてもそのトークン自体は有効期限が切れない限り無効にならないので、万一そのトークンが盗まれていたら悪用される危険性がある
認証のストラテジーにはその他、SSO(Single Sign On)やOAuthもある。ここでは割愛。
おわりに
次はセッションベース認証とトークンベース認証を実際に実装して比較してみたい。
参考文献
- HTTP Cookie の使用 - MDN
- セッションベース認証とトークンベース認証の違いを分かりやすくまとめてみる - Zenn
- ログイン/認証の仕組みをシンプルにまとめた - Qiita
- JWT認証 vs セッションID認証の違い - Qiita
- クッキーとセッションを雰囲気で使っているエンジニアが、違いを説明できるようになる記事 - Zenn
- Introduction to JSON Web Tokens - JWT
- Session Management Cheat Sheet - OWASP
- 安全なウェブサイトの作り方 - 1.5 クロスサイト・スクリプティング - IPA
- 安全なウェブサイトの作り方 - 1.6 CSRF(クロスサイト・リクエスト・フォージェリ) - IPA
- HTTP ヘッダー - MDN
- JWT・Cookieそれぞれの認証方式のメリデメ比較 - Qiita

