基本的な認証方式まとめてみた
概要
個人的に必要があったので、基本的な認証方式についてまとめてみました!
まとめた認証方式は以下です。
- Basic 認証
- Digest 認証
- クッキー・セッション認証
- トークンベース認証
Basic 認証
ユーザー名とパスワードを Base64 でエンコードしてサーバーに送信するだけの単純な認証方法
Base64 は可逆変換であるため、SSL/TLS 通信が必須となる。(傍受される危険があるため)
送信手段
ヘッダーのAuthorizationに以下のような方式でサーバーに送信
Authorization: "Basic {ユーザー名とパスワードをBase64でエンコードしたもの}"
メリット
- 実装が単純
デメリット
- 通信の暗号化が必須
Digest 認証
Basic 認証のセキュリティ強化版。
ユーザー名とパスワードをハッシュ化し、さらにサーバーから送られてくるランダムな値(nonce)を組み合わせることで、さまざまな攻撃に耐性のある認証方式
メリット
- 高セキュリティ
デメリット
- 実装が複雑
Basic 認証・Digest 認証は Web の認証としてはあまり使われていない。理由としては、
- リクエスト毎にユーザー名とパスワードを送信し、それを検証するコストがかかる(リクエスト毎に DB に問い合わせる)
- 明示的なログアウトができない
など
クッキー・セッション認証
ログイン時にセッション ID を発行し、クッキーで送受信する認証方式
送信方法
クッキー
ログイン
ユーザーはログインに必要なユーザー ID とパスワードを送信する。
ログインが成功したらセッション ID(セッショントークン)という一意な識別子を発行し、それをキーバリュー型のデータベース(Redis)などに保持しておく。
セッション ID はクッキーでクライアントに返され、リクエスト時にクッキーを再送することで、ログイン済みユーザーを判別できる。
ログアウト
セッション ID を破棄するだけで、ログアウトできる。
メリット
- ログアウト処理が容易にできる
デメリット
- クラインとは Web ブラウザが基本
- スケールするにはセッションストアを持つ必要がある
JWT
ログイン時にロインユーザー情報などを含んだトークンを発行し、リクエスト毎にサーバーに送信することで、ログインを判別する方式
クライアント側にユーザー情報を完全に委託でき、サーバー側はそれを取り出すだけで良い。このような特徴から近年の SPA やマイクロサービスで、一般的に使用される。
JWT は以下の構成できているいる。
- Header
- ヘッダーには署名の形式が格納されている。(アルゴリズム、タイプなど)
- Payload
- **JWT の正式仕様(RFC 7519)**では、Payload の中の各キーと値を「Claim(主張)」と呼ぶ
- よって、ライブラリでは
Claimsと表現されているっぽい。
- Signature
- ヘッダーとペイロードを署名したもの
- この Signature があるため、JWT は改ざん検知が可能
- ヘッダーやペイロードは Base64 でエンコードされているだけであるため、誰でも中身が覗ける(そのため、セキュリティに関する情報は含めるのは NG)
- よって、悪意のある第三者にヘッダーやペイロードが書き換えられることは可能
- しかし、Signature は秘密鍵を持つものしか署名できない。→ よって、サーバー側で
ヘッダー+ペイロードと署名を比較することで改ざん検知が可能
JWT はこれらを Base64 でエンコードし、ピリオドで区切った文字列になる。
- 例
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. ←ヘッダー
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0. ←ペイロード
KMUFsIDTnFmyG3nMiGM6H9FNFUROf3wh7SmqJp-QV30 ←署名
Header
署名の形式を格納
- 例
{
"alg": "HS256",
"typ": "JWT"
}
Payload
ペイロードにはログインしたユーザーの情報が格納されている
{
"sub": "12345",
"name": "John",
"exp": 123456
}
登録済みクレームとして以下のようなクレームがある。
| 名前 | 概要 |
|---|---|
| iss | トークンの発行者(認証サービス)の識別子 |
| sub | 認証したユーザーの識別子(つまり、ユーザー ID など) |
| aud | トークン受信者(サービスプロバイダー)の識別子 |
| exp | トークンの有効期限 |
| iat | トークンの発行日時 |
| jti | トークンごとの識別子 |
これら以外にもアプリケーション固有のクレームを追加することも可能
Signature
ヘッダーとペイロード + シークレット(任意の文字列)をヘッダーのアルゴリズムを用いて暗号化し、Base64 でエンコードしたもの。
署名アルゴリズム
| アルゴリズム | 特徴 | 主な用途 |
|---|---|---|
| HS256 | 共有鍵(Secret Key)方式 | シンプル・高速。小規模 API、内部 API 向き。 |
| RS256 | 非対称鍵(公開鍵/秘密鍵)方式 | OAuth2、OpenID Connect、複数サービス間認証でよく使う。 |
- HS256 の場合は、実装している通り、サーバーに秘密鍵(.env に記載している
JWT_SECRET_KEY)を持つのみで OK - RS256 はサーバーで秘密鍵によって署名、クライアントに公開鍵が必要
送信方法
ヘッダーのAuthorizationにトークンを含める
Authorization: "Bearer {トークン(JWT)}"
メリット
- サーバーはステートレスになる。(スケールしやすい)
- クライアントはなんでも良い
デメリット
- ログアウトが難しい
- ブラックリスト対応などが必要になる
トークン認証(JWT) vs クッキー・セッション認証
| 項目 | クッキーセッション | JWT(トークン認証) |
|---|---|---|
| ステート | サーバーに状態(セッション)を保持 | サーバーはステートレス(状態を保持しない) |
| スケーラビリティ | セッションストア(Redis など)が必要 | 水平スケールしやすい |
| 複数ドメイン | 制約が多い(Cookie の制限) | トークンなら自由 |
| クライアント自由度 | Web ブラウザ向き | モバイル・SPA(Single Page App)・API 向き |
| ログアウト処理 | サーバー側でセッション破棄 | JWT は難しい(ブラックリスト対応など必要) |
実装サンプル
Discussion