プログラミング初心者がアーキテクトっぽく語る

見苦しい記事も多数あるとは思いますが訂正しつつブログと共に成長していければと思います

Basic認証、Digest認証、Bearer認証、OAuth認証方式について

Basic認証、Digest認証、Bearer認証、OAuth認証方式はRFCで標準化されている認証方式の中で最もよく目にする方式だ。

Basic認証とDigest認証は多くのサーバ、クライントで実装されており導入障壁が低い認証方式だ。

機密性の高いデータを扱うサービスではよりBearer認証、OAuth認証方式を目にすることも多い。

ここではBasic認証、Digest認証、Bearer認証、OAuth認証方式について簡単に触れる。

この4つを概要を理解しておけば大体のWebサービスは理解できるだろう。

もしサービスが固有の認証方式を実装していた場合でもこれらの方式との類似性に着目すればすぐに理解できるはずだ。SAMLOpenIDと言ったより複雑な認証方式を理解する上でも助けにもなると考える。


1. Basic認証方式

最も理解しやすいのがBasic認証方式だ。RFC 2617で規定されている。

  • 動作
    1. ブラウザがあるページを要求する(この時点でクライアントは認証が必要なことを知らない)
    2. サーバがWWW-Authenticateヘッダに「Basic」をセットした401 Authorization Requiredを返信してBasic認証が必要なことを伝える
    3. ユーザがユーザ名とパスワードをブラウザに入力する
    4. ブラウザはユーザ名とパスワードを「:」でつなげてBase64エンコードしてAuthorizationヘッダにセットしてRequestを送る
    5. サーバはユーザ名とパスワードをBase64でデコードして認証を行う
    6. 認証が成功するとサーバは要求されたページを送る
    7. ブラウザは認証状態をキャシュし、キャッシュがクリアされるまでユーザが再認証を要求されることはない

上記の説明ではブラウザが前提だがWeb APIの場合は事前に認証が必要なことを知っているので手順1〜3がなくなる。

手順5で認証に失敗した場合は401 Authorization Required、認可に失敗した場合(=アクセス権がない) は403 Forbiddenが手順6で送られる。

ほぼすべてのWebサーバ、ブラウザ、クライアントツールで実装されているため簡単に導入できることがメリット。Python等でクライントコードを記述するのも容易だ。

Base64は暗号化ではないため簡単にユーザ名、パスワードを盗まれてしまう。Basic認証方式を採用する場合、HTTPSを利用することは必須条件となる。

ログアウトの仕組みがないためネットショップのようにセッションを厳密に管理したい用途には向かない。

Webサーバ内のディレクトリへのアクセス管理をする技術なため、Single Sign Onのようなサービスを跨いだ認証はできない。

実はAuthorizationヘッダではなくURLにユーザ名とパスワードを入れることもできる。以下に例を示す。しかしURLに機密情報を入れるのは「盗んでくれ」と言っているようなものなので例えAPIが受け付ける場合でも避けるべきだ。RejectするAPIが多い。

  http://username:password@www.example.com/users

環境によってはProxyが認証を代行していることもある。Proxyと認証する場合も使用するStatus Codeやヘッダが異なるだけで動きは同じだ。

  • 動作
    1. ブラウザがあるページを要求する(この時点でクライアントは認証が必要なことを知らない)
    2. ProxyがProxy-Authenticateヘッダに「Basic」をセットした407 Proxy Authentication Requiredを返信してBasic認証が必要なことを伝える
    3. ユーザがユーザ名とパスワードをブラウザに入力する
    4. ブラウザはユーザ名とパスワードを「:」でつなげてBase64エンコードしてProxy-AuthorizationヘッダにセットしてRequestを送る
    5. Proxyはユーザ名とパスワードをBase64でデコードして認証を行う
    6. 認証が成功するとProxyはサーバに要求を転送する
    7. サーバは要求されたページをブラウザに送る
    8. ブラウザは認証状態をキャシュし、キャッシュがクリアされるまでユーザが再認証を要求されることはない

Proxyが多段になっている場合はHTTPトンネリングを使った方がよいようだ。


2. Digest認証方式

Basic認証では盗聴が大きな問題になる。Digest認証方式は動作も特徴もBasic認証方式とほとんど同じだが、パスワードがランダムな文字列と組み合わせてハッシュされている点が異なる。

Digest認証方式はBasic認証方式と同じRFC 2617で規定されている。

  • 動作
    1. ブラウザがあるページを要求する(この時点でクライアントは認証が必要なことを知らない)
    2. サーバがWWW Authenticateヘッダに「Digest」とランダムな文字列(nounce)をセットした401 Authorization Requiredを返信してDigest認証が必要なことを伝える
    3. ユーザがユーザ名とパスワードをブラウザに入力する
    4. ブラウザはパスワードをnouceとクライアントが生成したランダムな文字列(cnouce)を組みあせてハッシュ(response)を計算する
    5. ブラウザはAuthorizationヘッダにユーザ名、nouce、cnounce、responseをセットしてRequestを送る
    6. サーバはユーザDB内のパスワード、nouce、cnouceからハッシュを計算してresponseと一致するか確認する
    7. 認証が成功するとサーバは要求されたページを送る
    8. ブラウザは認証状態をキャシュし、キャッシュがクリアされるまでユーザが再認証を要求されることはない

上記の説明ではブラウザが前提だがWeb APIの場合は事前に認証が必要なことを知っているので手順1〜3がなくなる。

パスワードそのものを送らないことでBasic認証よりセキュリティを強化しているが、世間一般から見れば脆弱極まりない認証方式だ。Digest認証方式を採用する場合はHTTPSを使うことは必須となる。


3. Bearer認証方式

Bearer方式はアクセストークンを利用する標準化された認証方式だ。RFC 6750で規定されている。

サービス固有の認証方式にはアクセストークンを非標準的な形で利用するものが多い。Bearer方式を理解すればこのような固有認証方式もスムーズに理解できるだろう。

  • 動作
    • クライアントは事前に入手したアクセストークンをtoken68形式でAuthorizationヘッダにセットしてRequestを送る
    • サーバはアクセストークンが有効なものか確認する
    • アクセストークンが有効だった場合、Webサーバはクライアントが要求したリソースを送る

手順2で認証が失敗した場合、サーバは401 Unauthorizedを返信する。このときWWW-Authenticateヘッダ内のerrorパラメータに失敗した原因が記載されている。

1つのアクセストークンで色々なサービスにアクセスできるようになっていることが多い(Google Sign-inなど)。Single Sign Onによく使われる。

ログアウト機能が実装できるのでセッション管理が重要なネットショップなどのサービスで利用できる。

上記の手順ではクライアントがアクセストークンをどのように入手したか記載されていないが、次に説明するOAuthを利用して手順1の直前に取得している。Bearer認証はOAuthの規格の一部だがこの2つは切り離して議論されることが多いのでここでも個別に取り上げている。


4. OAuth認証方式

アクセストークンの要求方法とそれに対する応答方法を標準化したものがである。Version 1がRFC 5849, Version 2がRFC 6749で規定されている。

  • 動作
    • クライアントが認証サーバに対してユーザ名とパスワードを送信してアクセストークンを要求する
    • 認証サーバはユーザの認証を行う
    • 認証が成功したら認証サーバは期限付きのアクセストークンを生成してクライアントに送る
    • クライアントはアクセストークンを利用してWebサーバとBearer認証やForm認証を行う

OAuthは認可の方式であり認証方式ではない。しかし認可には認証という正確もある。そこでOAuthを簡易的な認証手段に利用するようになり、これが一般的にOAuth認証方式と呼ばれている。

OAuthを純粋に認可方式として利用した技術としてはOAuthとOpenIDを組み合わせたOpenID Connectがある。優れた技術である一方、他の認証方式と比べると複雑なため利用ケースは限定的だ。