はじめに
この記事は長いです。言いたいことはおわりににまとめています。
「認証」とは、ユーザーやシステムが提示した情報をもとに、それが名乗っているとおりの相手であることを確認することです。たとえばシンプルなWebサービスなら、ログインフォームから入力されたIDとパスワードを登録済みの情報と照合し、正当性を確認できれば認証は完了します。
しかしHTTPはステートレスなので、一度認証したユーザーを次のリクエストと紐付ける仕組みがありません。そこで、リクエストごとに認証情報を提示する方式や、Cookieなどでセッション識別子を運び、サーバー側で認証状態を管理する方式が広く使われるようになりました。その後、第三者アプリへの権限委譲、組織やサービスをまたいだID連携、APIへのアクセスといった要件に応じて、サーバー側のストアを参照する参照型トークンや、検証に必要な情報を自身に含む自己完結型トークンも使われるようになります。現在では、パスワードの代わりに公開鍵暗号を用いるパスキーも普及しています。
こうした発展の過程で、さまざまな「〇〇認証」という言葉が使われるようになりました。しかし、それが指すものは、実際に本人確認しているものもあれば、上述の「ログイン状態の維持」でもあったり、認可する仕組みが誤用されて認証に使われていたりと、さまざまです。
そこで、この記事では、それぞれの方式について背景や仕組みを整理することで、「〇〇認証」の全体像を把握することを目的とします。
ざっくり分類
それぞれの「〇〇認証」でやっていることを大まかに分類すると、次の5つになる。
本人確認
ユーザーが「本人しか出せないもの」を実際に提示し、サーバーがそれを検証する仕組み。
対象: Basic認証 / Digest認証 / パスキー認証(FIDO2)
認証状態と権限の運搬
本人確認や権限の付与が済んだ後のやり取りで、その結果をセッションIDやトークンとして運び、値が有効かどうかを検証する仕組み。
HTTPはステートレスで、リクエストをまたいでユーザーを覚えておく仕組みを持たない。そのため、一度確認した結果を次のリクエストに引き継ぐ手段が必要になる。
その手段は、「何を運ぶか」と「どうやって運ぶか」によって、更に細分化される。
- 何を運ぶか: サーバー側のセッションを指す参照型の値(セッションID、opaque token)か、検証に必要な情報を自身に含む自己完結型の値(JWTなど)か。
-
どうやって運ぶか: ブラウザが自動で付与してくれるCookieか、クライアントが明示的に付与する
Authorizationヘッダー(Bearer)か。
対象: Cookie認証 / セッション認証 / トークン認証 / Bearer認証 / JWT認証
認可の仕組みの流用
認可(Authorization)は「このアプリに、この範囲の操作を許すか」を決める仕組みであり、「誰が操作しているか」を確かめる認証(Authentication)とは目的が異なる。
にもかかわらず、認可のために発行されたアクセストークンを提示できたことをもってログインを成立させる実装が広まった。そんな仕組み。
対象: OAuth認証
認証結果の連携
資格情報の検証はIdentity Provider(IdP)が行い、サービス側(SP / RP)は受け取った結果が本当にそのIdPから来た改ざんのないものかを署名で検証する、という分業でログインさせる仕組み。
対象: SAML認証 / OpenID Connect認証
クライアントの識別
クライアントに発行したキーをリクエスト時に提示させることで「どのアプリケーション・サービスからのリクエストか」を識別する仕組み。
利用者やプロジェクトの識別、利用量の把握とクォータ管理、課金、規約違反者の遮断といった、提供側の運用のための識別が主な目的である。エンドユーザーの本人確認とは目的そのものが異なる。
対象: APIキー認証
Basic認証
分類: 本人確認
概要
ユーザー名とパスワードをコロンで連結し、Base64エンコードしてリクエストヘッダーに乗せる、最もシンプルなHTTP認証スキームの一つ。
背景
国際研究機関であるCERN(欧州原子核研究機構)で、コンピューター上に分散した研究情報を共有・参照するという課題を解決するためWebの構想が生まれ、1990年に最初のWebブラウザ、Webサーバー、HTML、HTTPなどが実装された。
1991年にCERNの外へ公開されると、大学や研究機関を中心にWebサーバーの設置が広がり、サーバーが扱う文書も、誰に見せてもよいものばかりではなくなっていった。組織内の限られたメンバーにだけ公開したい文書や、更新を許す相手を絞りたい文書が出てくると、サーバー管理者には、文書ごとに閲覧・更新できる利用者を制御したいという要求が生まれる。しかし当時のHTTPには、リクエストの送り主が誰であるかを識別・確認する仕組みがなかった。そこで1993年、CERNの開発者によってBasic認証の仕組みが実装された。
Basic認証はその後広く利用され、1996年に公開されたHTTP/1.0の仕様に、既存の認証方式として収録された。
規格
RFC 1945(1996年5月:HTTP/1.0の一部として文書化) → RFC 2617(1999年6月:HTTP本体の仕様から分離し、Digest認証とあわせて1つの文書に整理) → RFC 7617(2015年9月:RFC 2617を廃止してBasicのみを定義。charsetパラメータによる文字エンコーディングの扱いなどを明確化)
認証方法
- クライアント:認証が必要なページにアクセスする。
- サーバー:
401 UnauthorizedとWWW-Authenticate: Basic realm="..."を返し、クライアントに認証情報の送信を促す。 - クライアント:標準のログインダイアログ(ポップアップ)を表示する。
- ユーザー:ダイアログにユーザー名とパスワードを入力する。
- クライアント:
username:passwordをコロンで連結してBase64エンコードする。 - クライアント:
Authorization: Basic <base64>を付けて、同じページにあらためてリクエストする。 - サーバー:ユーザー名とパスワードを検証する。
- サーバー:正しければアクセスを許可する。
以降のリクエストでも、クライアントとサーバーは5〜8を毎回繰り返す。
利点
- 仕組みが単純で、ほとんどのHTTPクライアント(ブラウザ、HTTPライブラリなど)が対応している。
- サーバー側にセッション状態を持たせる必要がなく、リクエスト単位で完結する。
問題点
- Base64は単なるエンコードであり暗号化ではないため、パスワードは実質平文で流れる。HTTPSなど暗号化の仕組みとの組み合わせが必須。
- 偽のサーバーに誘導されても、クライアントは相手を確かめずにパスワードを送ってしまう(フィッシング耐性がない)。
- ログイン画面はブラウザ標準のダイアログであり、見た目や挙動に関する柔軟性が低い。
- 「ログアウト」という概念が存在しない。
Digest認証
分類: 本人確認
概要
ユーザー名、パスワード等から算出したダイジェスト値(ハッシュ値)を送信して認証を行う、Basic認証と同じHTTP認証スキームの一つ。
背景
1990年代半ばのWebは通信路が平文のHTTPであることが前提で、SSL(TLS)による暗号化の導入は一般的ではなかった。Basic認証は認証情報をそのまま載せて送るため、経路上で盗聴されればパスワードが復元されてしまう。
そこで、通信路の暗号化を前提にできない環境でも、パスワードそのものをネットワークに流さずに済む方式が求められた。IETF(Internet Engineering Task Force:インターネット技術の標準化を担う国際任意団体)にてチャレンジ/レスポンス型のDigest認証が提案され、1997年にRFC 2069として標準化された。1999年にはBasic認証と同じRFC 2617で仕様が整理され、主要なWebサーバー・ブラウザにも実装された。
2000年代以降はTLSが広く普及し、「パスワードそのものを通信路に流さない」というDigest認証の利点が薄れたこともあり、採用されるケースは限定的。
規格
RFC 2069(1997年1月:チャレンジ/レスポンス型のDigest認証を最初に標準化) → RFC 2617(1999年6月:RFC 2069を廃止。攻撃対策を踏まえた改訂でqop・cnonce・ncなどを追加) → RFC 7616(2015年9月:RFC 2617を廃止。SHA-256/SHA-512-256に対応し、MD5は非推奨に)
認証方法
- クライアント:認証が必要なページにアクセスする。
- サーバー:一時的な値
nonceを生成する。 - サーバー:
401 Unauthorizedと以下のWWW-Authenticateを返し、クライアントに認証情報の送信を促す。WWW-Authenticate: Digest realm="...", # 保護空間の名前 nonce="abc123", # サーバーが生成する一時的な値 algorithm=SHA-256, # ハッシュアルゴリズム qop="auth" # 保護品質オプション。認証は「auth」 - クライアント:標準のログインダイアログ(ポップアップ)を表示する。
- ユーザー:ダイアログにユーザー名とパスワードを入力する。
- クライアント:ダイジェスト値のもとになる
A1・A2を組み立てる。A1 = username:realm:password A2 = HTTPメソッド:リクエストURI (例:"GET:/private/") - クライアント:
A1・A2とサーバーから受け取った値から、ダイジェスト値(response)を計算する。response = H( H(A1):nonce:nc:cnonce:qop:H(A2) ) - クライアント:計算した
responseと、その他の平文パラメータを送信する。Authorization: Digest username="...", realm="...", uri="...", nonce="...", nc=00000001, cnonce="...", qop=auth, algorithm=SHA-256, response="<ハッシュ値>" - サーバー:保管している
H(A1)から同じ方法でresponseを計算し、クライアントから送られてきた値と比較することで正当性を検証する。 - サーバー:正しければアクセスを許可する。
以降のリクエストでも、クライアントとサーバーは6〜10を毎回繰り返す。
利点
- パスワードそのものをネットワークに送信しない。
-
nc(nonce count)を利用することでリプレイ攻撃を検知できる。
問題点
- 認証情報は保護されるが、通信情報そのものは保護されない。通信を盗聴された場合は辞書攻撃につながる。
- 中間者がチャレンジをBasic認証などの弱いスキームに差し替えるダウングレード攻撃も成立するため、中間者攻撃への耐性は低い。
- サーバーは
H(A1)(username:realm:passwordのハッシュ)を保持する必要がある。このファイルが漏洩すれば、パスワードを復元されなくてもそのまま認証を突破されてしまう。 - ログイン画面はブラウザ標準のダイアログであり、見た目や挙動に関する柔軟性が低い。
- 「ログアウト」という概念が存在しない。
Cookie認証
分類: 認証状態と権限の運搬
概要
サーバーがレスポンスでSet-Cookieヘッダーを送り、クライアント(ブラウザ)がそれを保存して以降のリクエストに自動的に付与する「Cookie」の仕組みを、ログイン状態の維持に転用した認証方法。
背景
HTTPはステートレスであり、当初リクエスト間でユーザーを識別する手段がなかった。1994年、NetscapeのLou Montulliが、リクエストをまたいだユーザーの識別やオンラインショッピングのカート機能を実現する仕組みとしてCookieを考案した。ブラウザが値を保存し以降のリクエストに自動で付け直してくれるこの仕組みにより、開発者がURLやhiddenフィールドに識別子を埋め込まなくても状態を維持できるようになった。
その後、上記のCookieの性質を活かし、ログイン状態を維持するための識別子を運ぶ手段としてWebアプリケーションの認証に広く使われるようになった。
Cookieに格納する値は、サーバー側のセッションを参照する鍵(セッションID)のこともあれば(後述のセッション認証を参照)、署名や暗号化を施した自己完結型のトークンのこともある(後述のトークン認証を参照)。
規格
「Cookie認証」という認証方式を定めた規格はない。
認証方法
- ユーザー:ログインフォームにID・パスワードを入力する。
- ブラウザ:入力された認証情報をサーバーに送信する。
- サーバー:認証情報を検証する。
- サーバー:成功したらセッションIDなどの識別子を
Set-Cookieヘッダーに乗せて返す。 - ブラウザ:Cookieを保存する。
- ブラウザ:以降、
Domain・Path属性のスコープに合致し、かつSecure・SameSiteなどの条件を満たすリクエストで自動的にCookieを送信する。 - サーバー:受け取ったCookieの値が有効なセッション(またはトークン)に対応するか検証する。
- サーバー:対応していればそのユーザーからのリクエストとしてアクセスを許可する。
利点
- ブラウザが自動的に送信してくれるため、クライアント側でトークンの保存・付与を明示的に実装する必要がない。
-
Secure・HttpOnly・SameSiteなどの属性によって、ブラウザ側の保護を受けられる。
問題点
- Cookieはリクエストのたびにブラウザが自動で送信するため、罠サイト経由で本人になりすましたリクエストを送られる(CSRF)。SameSite属性やCSRFトークンによる対策が必要。
- HttpOnly属性を付けないとJavaScriptからCookieを読み取れるため、XSSの脆弱性があるとセッションIDを盗まれる。
- Secure属性を付けないとHTTP通信でもCookieが送信されるため、盗聴によってセッションIDを奪われ、セッションハイジャックにつながる。
- ログイン時にセッションIDを再発行しないと、攻撃者があらかじめ用意したIDをそのまま使わされる(セッション固定化攻撃)。
- CookieにセッションIDを入れる方式では、サーバー側でセッションを管理する必要があり、複数サーバー構成ではセッション情報を共有する仕組みが必要になる。
- 別ドメインのAPIとは相性が悪く、CORSの設定やサードパーティCookieの規制への対応が必要になる。
セッション認証
分類: 認証状態と権限の運搬
概要
認証済みのユーザー情報(セッション)をサーバー側で保持し、クライアントには推測困難なセッションIDのみを渡す認証方式。セッションIDの受け渡しには一般にCookieが使われる。歴史的にはhiddenフォームフィールドやURLに埋め込む方法も使われたが、URLに載せる方法はリンク・アクセスログ・ブラウザ履歴・Refererヘッダーなどから漏洩するため、OWASPはCookieの利用を推奨し、URL経由で送られたセッションIDを受け付けないよう求めている。API向けにはカスタムHTTPヘッダーで送る構成もある。
背景
当時Webアプリケーションのログインに使われていたBasic認証は、リクエストのたびに認証情報そのものを送る必要があり、明示的なログアウトもできないという問題を抱えていた。その後、状態保持の仕組みであるCookieを応用する形で、セッションを使った方式が1990年代後半に広まった。
一方で、プライバシーへの懸念からCookieを無効にする利用者や、Cookieに対応しない携帯電話などの端末も存在した。そのため、hiddenフィールドやURLにセッションIDを埋め込む方法も代替手段として併用された。
2000年代後半以降は、スマートフォンアプリやWeb APIの普及に伴い、HTTPヘッダーで認証情報を送る方法も使われるようになった。
規格
特定のRFCを持つ規格ではなく、Cookieやサーバーサイドのセッションストアといった複数の技術を組み合わせた実装パターンの総称。
認証方法
- ユーザー:ログインフォームにID・パスワードを入力する。
- ブラウザ:入力された認証情報をサーバーに送信する。
- サーバー:認証情報を検証する。
- サーバー:推測困難なセッションIDを生成する。ログイン成功時には必ず新しいIDを発行する(セッション固定化対策)。
- サーバー:セッションIDをキーに、メモリ・DB・Redis等のセッションストアへセッション情報を保存する。
- サーバー:セッションIDをCookie等でクライアントに返す。
- ブラウザ:以降のリクエストでセッションIDを送信する。
- サーバー:セッションIDをキーにセッションストアを参照する。
- セッションストア:対応するセッション情報を返す。
- サーバー:一致すればアクセスを許可する。
利点
- サーバー側でセッションを消すだけで即座に失効させ、強制ログアウトできる。
- クライアントに渡すのは推測困難なIDだけなので、機密情報をクライアント側に持たせずに済む。
問題点
- サーバー側に状態を持つため、複数台構成ではセッションストアの共有が必要になりスケールしにくい。
- セッションIDの盗難(セッションハイジャック)への対策が必要。
- 認証成功時に既存のセッションIDを無効化しないと、攻撃者が事前に仕込んだIDがログイン後もそのまま有効になり、セッション固定化(Session Fixation)攻撃が成立する。
トークン認証
分類: 認証状態と権限の運搬
概要
ログインなどで発行したトークンを、クライアントがリクエストごとに付与して提示し、サーバーがその値を検証してアクセスを許可する方式。送り方には主にAuthorizationヘッダー(後述のBearer認証)が使われ、トークンの形式には後述のJWTなどが使われる。
トークンには以下の2種類がある。
- 参照型(opaque token): 値そのものには意味がない乱数文字列。この値をキーとして、DBやRedisなどサーバー側のストアにユーザーIDや有効期限などを保存しておく。検証時はこのストアに問い合わせる必要がある代わりに、ストア側の記録を消すだけで即時失効できる。
- 自己完結型(JWTなど): 検証に必要な情報(ユーザーID、有効期限など)を署名付きでトークン自体に埋め込む。サーバー側にストアを持たず署名検証だけで済む代わりに、発行済みトークンの失効が難しい。
セッション認証との対比で「トークン認証」と言う場合は自己完結型を指すことが多いため、本記事でも後者を想定する。
背景
2000年代後半になると、Web APIを公開するサービスが増え、APIを呼び出すクライアントも、ネイティブアプリ・CLI・サーバー間通信・別ドメインのサードパーティアプリなど、ブラウザ以外に広がった。こうしたクライアントは、Cookieを介したセッション認証は利用できない。そこで、発行された値をクライアント自身が保持し、リクエストのたびに明示的に付与する方式が使われるようになった。
この方式は、2012年にOAuth 2.0とともにAuthorization: Bearerヘッダーで送る形が標準化されたことで、OAuthに限らず自社APIなどにも広く使われるようになった(詳細は後述のBearer認証)。ただし当初のトークンは参照型が一般的で、検証のたびにサーバー側のストアへ問い合わせる必要があった。
その後、SPAやマイクロサービスの普及とともに、1つのトークンを複数のAPIサーバーが受け取って検証する構成が広まった。この構成では、すべてのサービスがストアに毎回問い合わせるのは負担が大きい。そこで、署名を検証するだけで有効性を判断できる自己完結型トークンが利用されるようになった。(その形式がJWT。詳細は後述のJWT認証)。
しかし、ログアウトや不正利用の検知時に即時失効させるには、結局サーバー側での管理が必要となる。また、トークンの置き場所としてlocalStorageが利用されていたが、XSSで盗まれるリスクがあった。
そこで、2020年代にはブラウザにトークンを持たせず、BFF(Backend For Frontend)などのサーバー側で保持する構成が推奨されるようになった。
規格
特定のRFCを持つ規格ではなく実装パターンの総称。
認証方法
- クライアント:ID・パスワードでログインする、またはOAuthのフローを経る。
- 発行元サーバー:認証情報を検証する。
- 発行元サーバー:ユーザーID・有効期限・宛先などを記載し、秘密鍵(HMACなど共通鍵方式の場合は共有鍵)で署名したトークンを生成する。
- 発行元サーバー:トークンをクライアントに返す。
- クライアント:以降のリクエストの
Authorizationヘッダー等にトークンを付与して送信する。 - APIサーバー:発行元の公開鍵(または共有鍵)で署名を検証し、有効期限や宛先を確認する。
- APIサーバー:有効であればアクセスを許可する。
利点
- 署名を検証するだけで有効性を判断でき、検証のたびに発行元やストアへ問い合わせる必要がない。
- 検証用の公開鍵を配布すれば、複数のサービスがそれぞれ独立して検証できるため、分散構成でもスケールしやすい。
- スコープ・有効期限・宛先をトークン自体に記載できるため、サービス間通信や組織をまたいだアクセスに向く。
- ネイティブアプリ・CLI・サーバー間通信など、ブラウザのようなCookieの自動管理を前提にできないクライアントでも扱いやすい。
問題点
- 発行済みのものを即座に失効させるのが難しい。有効期限を短くし、リフレッシュトークンと組み合わせて運用するなどの対応が必要。
- トークンをlocalStorageなどJavaScriptから読み取れる場所に保存すると、XSSで盗まれる恐れがある。
OAuth認証
分類: 認可の仕組みの流用
概要
ユーザーが自分のID・パスワードを第三者アプリに渡すことなく、特定の権限(スコープ)と期間に限定してリソースへのアクセスを許可するための、認可(Authorization)のためのフレームワーク。「OAuth認証」とは、これを認証の仕組みとして誤用したものを指す。
背景
2000年代半ば、Web APIを公開するサービスが増加するにつれ、サードパーティアプリがユーザーに代わってサービス元のアカウント情報にアクセスし、機能を提供するようになった(例:Twitter APIを利用するTweetDeck)。しかし、当時これを実現する標準的な手段はなく、サードパーティアプリにサービス元のID・パスワードをそのまま入力させる「パスワード・アンチパターン」が横行していた。この方式では、アプリに全権限を無期限で渡すことになり、権限を絞ることも、あとから取り消すこともできない。
そこで、特定の範囲に限定し即時取り消しもできる権限を与える認可の仕組みを実現するため、Twitter等の開発者が検討を重ね、2007年にOAuth Core 1.0が公開された。その後、2008年からIETFで議論が始まり、2010年にRFC 5849として文書化された。
OAuth 1.0
「リクエストトークン取得 → ユーザーがサービス上で許可 → アクセストークン取得」という3段階の手順で、サードパーティアプリに権限委譲する仕組み。
しかしOAuth 1.0は、リクエストごとに署名を計算する必要があるなど実装の難易度が高かった。モバイルアプリの普及で多様なクライアントに対応する必要も生じたことから、署名を必須とせずHTTPSを前提にしたシンプルな仕組みへ刷新したOAuth 2.0が2012年に標準化された。
OAuth 2.0(2012年)
署名の計算を必須とせず、HTTPSによる通信路の暗号化を前提にトークン文字列をそのまま送るだけでよいシンプルな仕組みに刷新した。あわせてモバイルアプリ・SPAなど多様なクライアントに対応する複数の認可フローも整備された。
OAuthは前述のとおりサードパーティアプリに対する権限の制御を想定しており、リソースを取得するためのアクセストークンの扱い方について定義されていた。一方で、ログインしたユーザーが誰なのかを認証する仕組みは定義されていなかった。
それにも関わらず、「ユーザーがログインし、プロフィール情報へのアクセスを許可した」という事実をもって「本人確認できた」とみなすソーシャルログイン機能が一般化してしまった。
OAuthのアクセストークンが表しているのはリソースへのアクセス権限であり、そのトークンがどのクライアント向けに発行されたかを、クライアントが標準的な方法で検証できるようにはなっていない。そのため、攻撃者が別のクライアント向けに発行されたトークンを持ち込むことで、他人になりすませてしまう。
後述のOpenID Connectでは、認証に必要な要件が整理されており、ソーシャルログインではそちらを利用する必要がある。
規格
OAuth Core 1.0(2007年12月) → OAuth Core 1.0 Revision A(2009年6月:セッション固定化攻撃への対策として改訂) → RFC 5849(2010年4月:OAuth 1.0aをIETFがInformational RFCとして文書化) → RFC 6749(2012年10月:OAuth 2.0 Authorization Framework)・RFC 6750(2012年10月:Bearer Token Usage)・RFC 7636(2015年9月:PKCE)・RFC 8252(2017年10月)・RFC 9700(2025年1月:Implicitフローの非推奨化、PKCEの必須化 など)
認証方法
OAuth 2.0が定義しているのは、手順8のアクセストークン取得と、それをAPIに提示する方法(RFC 6750)までである。「OAuth認証」と呼ばれるものは、そのトークンで得たプロフィール情報をもって本人とみなす手順10〜11を、各サービスが独自に付け足したものにあたる。
以下は認可コードフローの場合である。
- ユーザー:クライアント上の「〇〇でログイン」ボタンを押す。
- クライアント:
client_id・事前に登録しておいたredirect_uri・scope(例:profile)を含めて、ユーザーを認可サーバーの認可エンドポイントへリダイレクトする。 - ブラウザ:認可エンドポイントにアクセスする。
- ユーザー:認可サーバー上でログインし、アプリへのアクセス許可(スコープ)を承認する。
- 認可サーバー:認可コードを発行し、事前に登録された
redirect_uriへリダイレクトで返す。 - ブラウザ:リダイレクト先のクライアントに認可コードを渡す。
- クライアント:認可コードをトークンエンドポイントに送る。
- 認可サーバー:アクセストークン(とリフレッシュトークン)を返す。
- クライアント:アクセストークンを使って、サービス側が用意したプロフィールAPIを呼ぶ。
〜OAuthの仕様が定めているのは、ここまで(トークンの取得と提示方法)〜 - リソースサーバー:トークンに紐付くユーザーID・メールアドレスなどを返す。
- クライアント:受け取ったユーザーIDを自サービスのアカウントと突き合わせ、「このユーザーがログインした」とみなしてログインを成立させる。
※stateの検証・PKCE・redirect_uriの完全一致検証などは省いています。
問題点
- 上記の通り認証の仕組みではないので本人確認には使えない。
Bearer認証
分類: 認証状態と権限の運搬
概要
AuthorizationヘッダーにBearer <トークン>という形式でトークンを載せて送る、HTTPの認証スキームの一つ。OAuth 2.0 のアクセストークンを送る方法として、RFC 6750 で定められた。
Bearerトークンとは、持っている人(bearer=持参人)なら誰でも使えるトークンである。持っていること自体が利用の資格になる。サーバーはトークンが有効かどうか(期限切れや失効していないかなど)は確かめるが、提示者がそのトークンを発行された本人かどうかは確かめない。
なお、巷で「Bearer認証」と呼ばれるものの実態はさまざまで、APIキーやJWTをAuthorization: Bearerで送るだけのものをそう呼ぶこともある。この記事では、RFC 6750 に沿って、OAuth 2.0 のアクセストークンを送る場合を中心に扱う。
背景
OAuth 1.0では、クライアントがリクエストごとに署名を計算して付ける必要があったが、実装が難しく、導入の障壁になっていた。
この反省から、OAuth 2.0の策定過程では、署名を必須とせず、HTTPS(TLS)による通信路の保護を前提に、トークン文字列をそのまま送る方式が検討された。一方、TLSだけに頼らない安全性を求める声も根強く、リクエストに署名を付ける方式を残すかどうかが論点となった。
結果的に、「取得したトークンをどう送り、どう扱うか」は本体から切り離し、別の仕様で定めることになった。こうして、署名を付けない方式と署名を付ける方式(MACトークンタイプ。最終的にRFCにはならず立ち消えになった)が、それぞれ別の仕様として検討された。
署名を付けない方式のトークンは、「有効なトークンを持っていれば誰でも使える(使う際に持っている人が正当な所持者かどうかは問わない)」という性質を持つため「Bearer(持参人)トークン」と呼ばれた。
2012年10月、OAuth 2.0本体の仕様であるRFC 6749と、Bearerトークンの仕様であるRFC 6750が同時に発行された。RFC 6749は、アクセストークンの取得方法を中心にOAuth 2.0の枠組みを定め、RFC 6750は、アクセストークンをAuthorizationヘッダーなどで送る方法と、アクセスを拒否するときのエラー応答(トークンが無効な場合のinvalid_token、権限が足りない場合のinsufficient_scopeなど)を定めている。
もっとも、Authorization: Bearerは送り方を定めたスキームにすぎず、載せる中身は問わない。後述のAPIキー認証やJWT認証でも、Bearerを使う実装はある。
規格
RFC 6750(2012年10月:OAuth 2.0 Bearer Token Usage)
認証方法
- クライアント:OAuthのフローやログインなどを経て、アクセストークンを要求する。
- 発行元:アクセストークンを発行する。
- クライアント:以降のリクエストの
AuthorizationヘッダーにBearer <トークン>という形式でトークンを付与して送信する。 - リソースサーバー:トークンを検証する(署名検証、DB照合、認可サーバーへの問い合わせなど)。
- リソースサーバー:有効であればアクセスを許可する。
- リソースサーバー:無効・期限切れであれば、
401 UnauthorizedとWWW-Authenticate: Bearer error="invalid_token"を返す。トークン自体が付いていない場合は、errorを付けずにWWW-Authenticate: Bearerだけを返す。
RFC 6750が定めているのは3以降であり、1・2のトークン発行はOAuthなど別の仕組みで行われる。
利点
- 実装がシンプル。
- Cookieと違いブラウザが自動送信しないため、CSRFの影響を受けにくい。
- ネイティブアプリ・CLI・サーバー間通信など、Cookieストアを持たないクライアントでも使える。
問題点
- トークンを提示できれば誰でも通るため、漏洩時の影響が大きい。
- 通信路の保護をHTTPSに依存する。
- ブラウザでlocalStorageなどJavaScriptから読める場所に保存すると、XSSで盗まれやすい。
JWT認証
分類: 認証状態と権限の運搬
概要
JWT(JSON Web Token)は、データ形式(フォーマット)の規格(RFC 7519)である。ユーザーIDや有効期限などの情報をJSONで表し、改ざん検知用の署名を付けて、1つの文字列にまとめたものである。
JWTはトークンの一種であり、クライアントがサーバーに送るときはAuthorization: Bearerヘッダーに載せることが多い。ブラウザ向けのWebアプリでは、Cookieに入れて送ることもある。
つまり、一般に「JWT認証」と呼ばれるものは、それ自体が独立した認証方式ではなく、トークンの形式にJWTを用いたものを指す俗称である。
背景
2010年前後、OAuth 2.0の策定などを通じて、サービス間でユーザーの情報や権限をやり取りする仕組みが広がった。OAuth 2.0では、アクセストークンをHTTPのAuthorizationヘッダーに載せて送るBearer認証(RFC 6750、2012年)が定められたが、トークンの中身の形式は規定されず、各サービスに任されていた。
こうした中で、HTTPヘッダーやURLのようにサイズや使える文字に制約のある場所へ、「誰についてのトークンか」「誰が発行したか」「いつまで有効か」といった情報(クレーム)を載せられる共通の形式が求められた。
そこで、JSONをベースにコンパクトで、URLに使える文字だけで表現できる形式としてJWTがIETFで策定され、署名(JWS)や暗号化(JWE)を定める関連仕様とともに2015年にRFCとなった。
標準化に先立ち、2014年に策定された後述のOpenID ConnectがIDトークンの形式としてJWTを採用したことは、JWTがID連携の分野で広く実装される重要な契機の一つとなった。その後、クラウドサービスのAPIやマイクロサービス間の情報連携、自社APIの認証トークンなどにも広く使われるようになった。
規格
・RFC 7519(2015年5月:JWT本体の仕様)
・RFC 7515(2015年5月:JWS、署名の仕様)
・RFC 7516(2015年5月:JWE、暗号化の仕様)
・RFC 7517(2015年5月:JWK、鍵をJSONで表す仕様)
・RFC 7518(2015年5月:JWA、algで指定する署名・暗号化アルゴリズムを定義)
・RFC 8725(2020年2月:既知の攻撃と対策)
認証方法
全体の流れはトークン認証の自己完結型と同じで、送り方もBearer認証やCookie認証と変わらない。JWTに固有なのは、トークンの構造と、受け取った側での検証手順である。
署名付きのJWT(JWS形式)は、Base64URLエンコードしたヘッダーとペイロードを.でつないだ文字列に対して署名を計算し、その署名もBase64URLにして末尾に.でつないだ文字列である。
eyJhbGciOiJSUzI1NiIs...(ヘッダー).eyJpc3MiOiJodHRwczov...(ペイロード).SflKxwRJSMeKKF2Q...(署名)
デコードすると、ヘッダーとペイロードは次のようなJSONになる。
// ヘッダー
{ "alg": "RS256", "typ": "JWT", "kid": "key-2026-01" }
// ペイロード(クレーム)
{ "iss": "https://auth.example.com", "sub": "user123", "aud": "https://api.example.com", "exp": 1767225600 }
APIサーバーは、受け取ったJWTを次の手順で検証する。
-
.で3つに分割し、ヘッダーとペイロードをデコードする。 - ヘッダーの
alg(署名アルゴリズム)が、あらかじめ受け入れると決めた方式かを確認する。 -
kid(鍵ID)などをもとに検証用の鍵を選び、署名を検証する。 -
iss(発行者)が信頼する発行者と一致するか、aud(宛先)に自分の識別子が含まれるか、exp(有効期限)が過ぎていないかを確認する。 - すべて問題なければ、
sub(ユーザー識別子)などでユーザーを識別し、アクセスを許可する。
利点
- 署名を検証するだけで有効性を判断できるため、発行元への問い合わせやセッションストアの共有が不要になる。
- 公開鍵方式で署名すれば、検証用の公開鍵を配るだけで、発行者とは別のサービスでも独立して検証できる(例:ECサイトで、注文・在庫・決済などに分かれた各APIが、共通の認証サーバーが発行したJWTを、認証サーバーに問い合わせずにそれぞれ検証する場合)。
問題点
- 一般に使われるJWS形式では、ペイロードは署名されるだけで暗号化されず誰でも読めるため、機密情報を含めてはならない(JWEを使えば暗号化できる)。
-
algやiss・audの検証を怠ると、署名のないJWTや、別の発行者・別のサービス向けのJWTを受け入れてしまう。 - 発行済みトークンを即時に失効させるのが難しい。有効期限を短くしてリフレッシュトークンと組み合わせる等の対応が必要になる。
SAML認証
分類: 認証結果の連携
概要
XML形式のアサーション(表明)を使って、Identity Provider(IdP)からService Provider(SP)へ認証結果を伝達する、XMLベースのSSO(シングルサインオン)標準。
背景
1990年代後半、企業内でWebベースの業務システムが増えると、システムごとにID・パスワードを管理する負担が問題になった。これを解消するため、一度のログインで複数のWebアプリを使えるWeb SSO製品が登場した。これらの多くはCookieでログイン状態を保持していたが、Cookieは異なるドメインには送信されないため、ドメインをまたぐSSOは各製品が独自の方式で実現していた。
この独自方式は、1社の中で同じ製品に揃えられる場合は問題ない。しかし、B2B取引などで取引先のサイトとの間でSSOを行おうとすると、相手は別の製品を使っていることが多く、連携できなかった。そこで、認証はIdPだけが行い、SPには「このユーザーは、いつ、どの方法で認証済みである」という署名付きの証明(アサーション)をドメインをまたいで渡す、フェデレーション(ID連携)の仕組みが求められた。アサーションは異なる製品の間でやり取りされるため、ベンダーに依存しない共通の形式で表現する必要があった。
当時はXML(とSOAP)によるWebサービス連携の標準化が進んでおり、XMLに署名を付与する仕様(XML署名)もW3CとIETFで策定が進められていた。この流れを受け、アサーションはXMLで表現し、XML署名で改ざんを検知できるように設計された。2001年、標準化団体OASISで策定が始まった。2002年2月にXML署名がW3C勧告となり、同年11月にSAML 1.0が標準化された。
SAML 1.0 / 1.1(2002年 / 2003年)
アサーションの中に入る「認証」「属性」「認可判定」の3種類のステートメント(記述)と、ブラウザを経由してIdPからSPへアサーションを届ける手順(Browser/POST、Browser/Artifact)を定義した。ただしIdPを起点とするフローが中心で、SP側から認証を要求する手順やシングルログアウトは標準化されていなかった。
SAML 1.xの時期には、他にも2つの取り組みが並行して進んだ。
1つは、Microsoftの.NET Passportへの対抗である。Passportは、Microsoftが管理する1つのアカウントで提携サイトにログインできるサービスで、ユーザーのIDをMicrosoft1社が握る形だった。これに対して、Sun Microsystemsなどの企業はLiberty Alliance(2001年設立)を結成し、各企業が自社でIDを管理したまま企業同士で連携できる仕様「ID-FF」を、SAMLを土台に策定した。
もう1つは学術分野で、米国の学術ネットワークInternet2が、大学の学内アカウントで他機関のリソースにアクセスするための「Shibboleth」をSAMLベースで開発した。
こうしてSAMLから派生した仕様が並立したため、2005年にこれらを統合したSAML 2.0が策定された。
SAML 2.0(2005年)
SAML 1.1、Liberty ID-FF 1.2、Shibbolethの成果を統合した。SPを起点とする認証要求(AuthnRequest)、シングルログアウトなどが追加された。また、SAML 1.0の後に勧告されたXML暗号化(2002年12月)を取り入れ、アサーションや属性の暗号化にも対応した。
2000年代後半からSalesforceやGoogle Apps(現Google Workspace)などのSaaSが普及すると、SAMLは「社内のActive Directoryで認証し、各種SaaSにSSOする」構成を実現する、企業向けSSOの標準となった。
ただし、SAMLはXML署名の検証に複雑な処理を要するうえ、ブラウザを介したWebアプリへのログインを前提としているため、モバイルアプリやAPI連携には向かなかった。API認可では2010年前後からOAuthが広く使われており、2012年にOAuth 2.0として標準化された。2014年にその上で認証を行うOpenID Connectが標準化されると、モバイルアプリやAPI連携の領域ではOAuth 2.0とOpenID Connectの組み合わせが主流となった(詳細は後述のOpenID Connect認証)。
規格
SAML 1.0(2002年11月:OASIS) → SAML 1.1(2003年9月:1.0の細部を修正した小改訂) → SAML 2.0(2005年3月:SAML 1.1・Liberty ID-FF 1.2・Shibbolethを統合)
認証方法
- ユーザー:SPが提供するサービスにアクセスする。
- ブラウザ:SPにリクエストを送る。
- SP:未認証であれば、認証要求(AuthnRequest)を添えてユーザーをIdPへリダイレクトする。
- ブラウザ:IdPにAuthnRequestを渡す。
- ユーザー:IdP上でログインする。
- IdP:認証結果を含むSAMLアサーション(XML)を生成し、デジタル署名を付与する。
- IdP:アサーションをSPへ送信するHTMLフォームをブラウザに返す。
- ブラウザ:SPの受け口(ACS)にアサーションをPOSTする。
- SP:受け取ったアサーションの署名・発行者・宛先・有効期限を検証する。
- SP:正当であればログインを許可する(以降はセッションCookie等でログイン状態を維持する)。
利点
- ドメインやベンダーが異なるIdPとSPの間でもSSOできる。
- 認証はIdPだけが行うので、各サービスにパスワードを預けずに済む。
- 多要素認証などのポリシーやアカウント停止を、IdPで一元管理できる。
問題点
- XMLベースで仕様が複雑。
- ブラウザのリダイレクトとPOSTを前提とするため、ネイティブアプリやSPAとの相性が悪い。
APIキー認証
分類: クライアントの識別
概要
サービスやアプリケーションを識別するための固定的な文字列(APIキー)をリクエストのヘッダーやクエリパラメータに含めて送信する方式。
エンドユーザー本人を確かめる手段ではなく、主目的は「どの利用者・どのアプリからの呼び出しか」を見分けることにある。
背景
2000年代に入り、自社の機能やデータを外部の開発者向けにWeb APIとして公開するサービスが広がると、提供側には、利用者やプロジェクトの識別、利用量の把握とクォータ管理、課金、規約違反者の遮断といった運用が必要になった。そこで、利用登録時にサービスごとの形式でAPIキーやアクセスキーを発行し、リクエストに付与させる方式が広く使われるようになった。
APIキーと呼ばれるものにはいくつかの形態がある。値そのものを秘密の資格情報として扱い、リクエストに載せて提示するもの、クライアント側のコードに埋め込まれ公開されることを前提として、Refererやアプリ署名で利用元を制限するもの、AWSのアクセスキーIDのように、署名用の秘密鍵と組み合わせて使う識別子などである。
このうち秘密の資格情報として扱う形態は、値を知っていれば誰でもそのアプリとしてAPIを呼び出せてしまう。そのため2010年代には、アクセスログやRefererヘッダー、公開リポジトリへの誤コミットによってキーが漏洩し、不正利用される事故が問題になった。これを受けて、ヘッダーでの送信、キーごとの利用範囲の制限、漏洩したキーの自動検出(GitHubのSecret Scanningなど)といった対策が整えられていった。
規格
統一されたRFCは存在せず、各サービスが独自に実装している。
認証方法
- 開発者:サービスの管理画面などでAPIキーの発行を申請する。
- 管理画面:APIキーを発行する。
- 開発者:発行されたAPIキーをクライアントに設定する。
- クライアント:リクエストヘッダー(例:
X-API-Key、Authorization: Bearer <キー>)やクエリパラメータにAPIキーを付与して送信する。 - APIサーバー:受け取ったAPIキーが有効かどうかをキーのストアと照合する。
- キーのストア:キーに紐付く利用者やレート制限などの情報を返す。
- APIサーバー:有効であればアクセスを許可する。
利点
- ヘッダーにキーを付けるだけで実装でき、導入が簡単。
- サーバー間通信で扱いやすい。
問題点
- 持っていれば誰でも使え、有効期限がないことも多いため、漏洩すると無効化するまで悪用され続ける。
- サービスによっては権限を細かく制御できない。
- アプリを使うエンドユーザーごとの認証・認可には向かない。
OpenID Connect認証
分類: 認証結果の連携
概要
OAuth 2.0の上に認証レイヤーを追加した、ID連携(Identity Federation)のためのプロトコル。OpenID Provider(OP。OAuthの認可サーバーを兼ねる)が行った認証の結果を、クライアント(Relying Party、RP)が検証できる形で受け取れるようにする。
OAuth 2.0を認証に流用した場合、アクセストークンが「どのクライアント向けに発行されたか」を検証する手段がなく、別のアプリ向けに発行されたトークンを使ったなりすましが可能だった。OpenID Connectでは、JWT形式の「IDトークン」に宛先のクライアント(aud)や発行者(iss)を含め、署名付きで発行することで、これを防いでいる。
背景
2000年代半ば、個人ブログの普及に伴い、他人のブログにコメントを残し合う文化が広がった。しかし当時のコメント欄は名前とURLを入力するだけのものが多く、誰でも他人を名乗れたため、コメント主が本当にそのURLの持ち主かを確認する手段がなかった。Microsoftの.NET Passportのように単一企業がIDを一元管理する仕組みは存在したが、特定企業への依存が敬遠され、誰でも自分でIdPを運用できる方法が模索されていた。
そこで2005年、LiveJournal(ブログサービス)の作者Brad Fitzpatrickが、自分のブログのURLをそのまま識別子として使い、そのURLの持ち主であることをIdPに証明させるOpenIDを考案し、LiveJournalに実装した。その後、同時期に提案されていた類似の方式と統合され、2007年にOpenID 2.0が策定されるとともに、標準化を担うOpenID Foundationが設立された。
OpenID 1.0(2005年)
ユーザーが管理するURL(例:https://example.com/alice)を識別子とする。サイトはそのURLのページに記載されたIdPへユーザーをリダイレクトし、URLの持ち主であることを証明する署名付きの結果を受け取る。
OpenID 2.0(2007年12月)
個人のURLの代わりに、IdPの識別子(例:yahoo.com)を入力してログインできる「OP Identifier」を導入し、自分のURLを持たない一般ユーザーでも使えるようにした。これを機にYahoo!やGoogleなどの大手が対応し、普及が進んだ。
しかし、OpenIDは独自のプロトコルであったため実装の負担が大きく、ブラウザのリダイレクトとHTMLフォームを前提とする仕組みはモバイルアプリとの相性も良くなかった。
一方、前述のとおりOAuth 2.0の認可コードフローを流用した独自の認証機能の実装が各社で乱立し、トークンの発行先を検証できないことに起因する脆弱性が指摘されていたという課題もあった。
OpenID Foundationは、この流れを踏まえ、すでに広く実装されていたOAuth 2.0を土台として、IDトークンやnonceといった認証に必要な要素を標準化した。
OpenID Connect(2014年)
OAuth 2.0の上に認証レイヤーを追加した仕様。openidスコープを指定すると、OpenID Provider(OP)が「どのユーザーを・どのクライアント向けに・いつ」認証したかを署名付きで表明するIDトークン(JWT)を発行する。リプレイ攻撃対策としてnonceパラメータも導入された。
OpenID 2.0との互換性はない。
以降、ソーシャルログインの事実上の標準実装となっている。
規格
OpenID Connect Core 1.0(2014年2月:OpenID Foundationにより初版発行。以降複数回更新)
・OpenID Connect Discovery 1.0(/.well-known/openid-configurationから各エンドポイントのURLや署名検証用の公開鍵(JWKS)の所在を自動で取得する仕様)
認証方法
以下は認可コードフローの場合である。ほかに、リダイレクトでトークンを直接受け取るImplicitフローやハイブリッドフローもあるが、RFC 9700は認可レスポンスでアクセストークンを返す方式を避けるよう求めており、現在は認可コードフロー(PKCE併用)が基本となっている。
- RP:OAuth 2.0の認可リクエストに
openidスコープを含めて、ユーザーをOPの認可エンドポイントへリダイレクトする。 - ブラウザ:認可エンドポイントにアクセスする。
- ユーザー:OP上でログインし、許可を承認する。
- OP:認可コードを発行し、リダイレクトで返す。
- ブラウザ:リダイレクト先のRPに認可コードを渡す。
- RP:認可コードをトークンエンドポイントに送る。
- OP:アクセストークンに加えて、認証したユーザーと認証イベントを表す署名済みのIDトークン(JWT)を発行する。IDトークンには
iss(発行者)・sub(ユーザー識別子)・aud(宛先クライアント)・exp(有効期限)・iat(発行時刻)が含まれる。 - RP:IDトークンの署名と、
iss(発行者)・aud(自分宛てか)・exp(有効期限)を検証する。 - RP:
issとsubの組み合わせでユーザーを識別し、自サービスのログインを成立させる。 - RP:
nameやemailといったプロフィール情報が必要な場合は、UserInfoエンドポイントに問い合わせる。 - OP:要求されたスコープに応じたプロフィール情報を返す。OPの実装によっては、これらが7のIDトークンに含まれることもある。
10・11は必要な場合のみ行う。
※state・nonceの検証、PKCE、redirect_uriの完全一致検証などは省いています。
利点
- OAuth 2.0の上に載るので、既存の認可基盤・ライブラリ・IdP製品をそのまま流用できる。
- IDトークンがJWTで自己完結しており、署名検証だけで身元を確認できる。
問題点
- 認可の枠組みの転用ゆえ実装事故が起きやすい(トークン検証漏れ、
nonce・stateの未検証など)。
パスキー認証(FIDO2)
分類: 本人確認
概要
FIDO2は、公開鍵暗号方式を用いて本人確認を行うための標準規格群。W3Cが策定するブラウザAPI「WebAuthn」と、FIDO Allianceが策定する認証器との通信プロトコル「CTAP」から構成される。
「パスキー」は、FIDO2/WebAuthnのクレデンシャル(鍵ペア)のうち、認証器側に保存され、ユーザーIDの入力なしでログインに使える「Discoverable Credential」を指す呼称。
秘密鍵をクラウド経由で複数のデバイスに同期する「同期パスキー」と、生成したデバイスの中にとどまる「デバイス固定パスキー」の2種類がある。
背景
2000年代以降、パスワードの使い回しやフィッシングによるアカウント侵害が繰り返され、パスワードだけに依存しない強固な認証が求められるようになった。
金融機関や企業では、RSA SecurIDなどのワンタイムパスワードトークンや、USB型のPKIトークンが以前から利用されていたが、製品ごとに導入方法や認証基盤が異なり、一つの認証器を複数のWebサービスで共通して利用できる環境が整っていなかった。また、サービス側には認証器の配布・登録・再発行などの運用負担があり、ワンタイムパスワードにも、偽サイトへ入力させたコードを攻撃者が即座に転送するリアルタイムフィッシングを防げないという限界があった。
2012年7月、端末や認証器のベンダーに依存しない共通仕様を策定し、強固な認証をさまざまなオンラインサービスから利用できるようにするため、PayPal、Lenovoなど6社によってFIDO Allianceが設立された。
FIDO Allianceは、既存のパスワード認証を段階的に強化する方式(= U2F)と、パスワードを使わずに認証する方式(= UAF)を並行して開発した。
「U2F」は、GoogleとYubicoが2010年代初頭から開発していた、公開鍵暗号を利用してフィッシングを防ぐセキュリティキーの仕組みがベースとなっている。両社は2013年にFIDO Allianceへ参加した。
U2F(2014年)
パスワードに加えて、セキュリティキーを2つ目の要素として使う仕様。セキュリティキーはサービスごとに鍵ペアを作り、公開鍵だけをサービスに登録する。秘密鍵はキーの中に留まり、ログイン時はそれで署名するだけなので、サーバーから情報が漏れても悪用されにくい。また署名には接続先のドメインが含まれるため、偽サイトでは使えず、フィッシングにも強い。
2014年10月、GoogleがChromeとGoogleアカウントでSecurity Keyの提供を開始し、同年12月にはU2F 1.0がUAF 1.0とともに正式公開された。これにより、共通仕様に対応した一つのセキュリティキーを、複数のサービスで利用できるようになった。
ただし、当時のU2Fは、知識情報(パスワード)を補強するための所持情報(セキュリティキー)という立ち位置であり、単独でユーザーを認証するものではなかった。
一方、「UAF」は、Nok Nok Labsが開発していたプロトコルがベースとなっている。同社はPayPalとともに、指紋センサーをオンライン認証に活用する仕組みを業界標準とするための団体の設立を呼びかけ、FIDO Allianceの創設メンバーとして参加した。
UAF(2014年)
端末のPIN、指紋、顔などでユーザーを確認し、スマートフォンなどの端末内の認証器がサービスから送られたチャレンジへ署名するパスワードレス認証の仕様。
UAFは、端末やアプリにFIDO UAFクライアントを組み込む必要があり、主にスマートフォンのネイティブアプリで利用された。Webサービスがブラウザの標準APIから利用できる仕組みではなく、Web全体へ展開するには、ブラウザとOSが共通して提供するAPIが必要だった。
そこでFIDO Allianceは、U2Fで実用化されたWeb向けの公開鍵認証を拡張し、外部セキュリティキーだけでなく、PCやスマートフォンに内蔵された認証器もブラウザから利用できる「FIDO2」の開発を進めた。
FIDO2では、Webサイトとブラウザの間、ブラウザやOSと外部認証器の間を、それぞれ独立した仕様として分離した。前者は「WebAuthn」、後者は「CTAP」として標準化された。
WebAuthn(2019年3月:W3C Level 1勧告)
Webサイトがブラウザに対し、公開鍵資格情報の作成や、チャレンジへの署名を要求するためのWeb API。
ブラウザとOSが利用可能な認証器の選択や操作を仲介する。
CTAP
ブラウザやOSと、セキュリティキーなどの外部認証器との通信を定めるプロトコル群。
- CTAP1:従来のU2F。主にパスワードの第2要素として利用する
- CTAP2:ユーザー検証や認証器内への資格情報の保存などを追加し、パスワードレス認証にも対応する
FIDO2(2018年〜2019年)
WebAuthnとCTAPを組み合わせた認証方式の総称。WebAuthnとCTAP2によるパスワードレス認証や、WebAuthnとCTAP1を使った従来のU2F認証に対応する。
FIDO2は常にパスワードレスになるわけではなく、第2要素、パスワードレス認証、多要素認証のいずれにも利用できる。2018年から2019年にかけて主要なブラウザとOSへの実装が進み、Webサイトはブラウザの標準APIから、端末内蔵の認証器や外部セキュリティキーを利用できるようになった。
しかし、秘密鍵がデバイスから出ない設計は安全である反面、デバイスを紛失・故障するとログインできなくなる。かといって、サービスごとに予備の認証器を登録しておく運用も現実的ではなかった。
この問題を解決するため、2022年5月、Apple・Google・Microsoftは、秘密鍵を暗号化したままクラウドで同期する「マルチデバイスFIDOクレデンシャル」と、手元のスマートフォンを使って近くの別デバイス(OS・ブラウザを問わない)にサインインする仕組みへの対応を共同で発表した。iCloudキーチェーンやGoogleパスワードマネージャーが同期の受け皿となり、同年10月にはGoogleがAndroidとChromeで実装を開始するなど、主要OS・ブラウザへの実装が一気に進んだ。
この過程で、Appleが2022年6月に用いた「パスキー」という呼称が広まった。当初は同期されるクレデンシャルを指す言葉だったが、その後、セキュリティキーのようにデバイスから出ないクレデンシャルも含む総称として整理された。
同期パスキー(synced passkey)
秘密鍵を暗号化してクラウドに預け、同じアカウントでサインインしている複数のデバイスに同期するパスキー。デバイスを紛失しても、ほかのデバイスから引き続き利用できる。
デバイス固定パスキー(device-bound passkey)
秘密鍵が、生成したデバイスの外に出ないパスキー。多くはセキュリティキーなどのハードウェア認証器に保存される。登録時のアテステーション(認証器の出自を示す署名付きの情報)で機種を識別できるため、高い保証レベルが求められる業務利用などで使われる。
規格
FIDO U2F 1.0(2014年)
→ CTAP 2.0(2018年2月:FIDO Alliance、Implementation Draft。2019年1月にProposed Standard)
→ WebAuthn Level 1(2019年3月) → WebAuthn Level 2(2021年4月) → WebAuthn Level 3(2026年8月:W3C勧告。パスキーの同期状態を示すフラグなどを定義)
認証方法
登録
- ブラウザ:登録を開始する。
- サーバー:チャレンジ(ランダムな値)とRP ID(サイトのドメイン)を返す。
- ブラウザ:アクセス先のオリジンがRP IDに対して正当かを確認したうえで、RP IDと、オリジン・チャレンジを含むデータのハッシュを認証器に渡し、鍵ペアの生成を要求する。この「オリジンの確認はブラウザが行う」点が、偽サイトでは署名を得られない理由になっている。
- ユーザー:認証器の要求に応じてユーザー操作を行う。最低限、UP(User Presence:認証器に触れるなどの存在確認)が必要で、生体認証やPINによるUV(User Verification:本人確認)を求められることもある。
- 認証器:サイトごとに異なる鍵ペアを生成する。秘密鍵はデバイス内に保持する。同期パスキーの場合は、秘密鍵を暗号化した状態でほかのデバイスにも同期する。
- 認証器:公開鍵とクレデンシャルIDをブラウザに返す。
- ブラウザ:公開鍵などをサーバーに送信する。
- サーバー:チャレンジとオリジンが期待どおりかを確認し、公開鍵をユーザーに紐付けて保存する。
認証
- ブラウザ:ログインを開始する。
- サーバー:チャレンジ(ランダムな値)を送る。
- ブラウザ:アクセス先のオリジンがRP IDに対して正当かを確認したうえで、RP IDと、オリジン・チャレンジを含むデータのハッシュを認証器に渡し、署名を要求する。
- ユーザー:認証器の要求に応じて、UVまたはUPを行う。
- 認証器:チャレンジやオリジンを含むデータに秘密鍵で署名する。
- 認証器:署名と、UP・UVをそれぞれ行ったかを示すフラグをブラウザに返す。
- ブラウザ:署名などをサーバーに送信する。
- サーバー:登録済みの公開鍵で署名を検証し、チャレンジ・オリジン・フラグが期待どおりかを確認する。
- サーバー:正当であればログインを許可する。
利点
- フィッシングに強い。認証情報がサイトのドメインに紐付いているため、偽サイトではログインできない。
- サーバーには公開鍵しかないので、漏洩しても悪用されにくい。
- サイトごとに別の鍵を使うので、パスワードの使い回し問題がない。
- 同期パスキーなら、端末をなくしても同期済みの別の端末でログインできる。
問題点
- 対応デバイス・ブラウザが必要。
- デバイス固定パスキーの場合、端末や物理キーをなくすとログインできなくなる。
- 同期先のアカウント(Apple ID、Googleアカウントなど)が乗っ取られると、パスキーもまとめて危険にさらされる。
おわりに
この長い記事を最後まで読んでくださった方も、はじめにから一気に飛んできた方も、どちら方もありがとうございます。
一口に「認証」と言っても、それが指すものの性質やレイヤーがバラバラすぎて、書きながら頭が痛くなりました。
この「認証」という言葉の曖昧さを踏まえると、この言葉を扱うときは、それが何を意味しているのかを意識しておいたほうがよさそうです。
なぜなら、あなたが「JWT認証」と呼んでいるものは、トークンの形式に着目すれば「JWT認証」、送り方に着目すれば「Bearer認証」、仕組み全体で言えば「トークン認証」、そして起きていることをそのまま書けば「パスワードで本人確認して、JWTでログイン状態を維持している」であり、どれも同じ1つの実装を別の層から見ているだけだからです。