🍣

基本的な認証方式まとめてみた

に公開

概要

個人的に必要があったので、基本的な認証方式についてまとめてみました!
まとめた認証方式は以下です。

  • 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 ←署名

署名の形式を格納

{
	"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 は難しい(ブラックリスト対応など必要)

実装サンプル

https://github.com/Yuji-Momotani/Go-Auth

GitHubで編集を提案

Discussion