HTTPクッキーと認証
Last updated
Was this helpful?
Last updated
Was this helpful?
ここまで述べたようにHTTPはステートレスなプロトコルであり、クライアント状態をサーバが保存することはない。しかし現実のアプリケーションでは、サービスへのログインなど、何らかのクライアント状態によってサーバの振る舞いを変えるニーズが存在する。
本章ではまずHTTPクッキーについて述べ、クライアント状態の典型例であるユーザ認証の実現方法をいくつか示す。
HTTPクッキー(HTTP cookie)は、Webサーバ上の情報をWebクライアントに保存しHTTPで利用するための汎用技術であり、これによりHTTPにおいて状態を実現することができる。1994年に、当時大きなシェアを持っていたWebブラウザNetscape Navigatorの開発元であるNetscape Communications社によって提案・実装された。その後IETFによる国際標準化が行われ、2011年に発行されたが最新版である。
HTTPクッキーは、レスポンスメッセージにおけるSet-Cookie
ヘッダ、リクエストメッセージにおけるCookie
ヘッダという2つのヘッダによって実現される。あるWebサーバからSet-Cookie
ヘッダを付けてレスポンスメッセージを送信すると、以降、このレスポンスメッセージを受け取ったWebクライアントからそのWebサーバへリクエストメッセージを送信するときにはCookie
ヘッダを付け、その中に、Set-Cookie
ヘッダに含まれていた情報を含める。
例を示そう。Webブラウザからhttp://example.jp/sample
にHTTPリクエストを送信したところ、そのレスポンスに次のヘッダが含まれていたとする。
このヘッダの中身SID=31d4d96e407aad42
がHTTPクッキーであり、この例では名前SID
の値が31d4d96e407aad42
であることを示している。
このレスポンスメッセージを受け取ったWebブラウザは、以降、http://example.jp/sample
にHTTPリクエストを送信する場合には次のヘッダを必ず付ける。
HTTPクッキーには、有効期限、URIに関する有効範囲(ホスト名、パス)、セキュリティ上の制限(HTTPSでのみ有効、JavaScriptからのアクセス不可)などを付与することができる。次の例は、有効期限をグリニッジ標準時2021年6月9日10時18分14秒までとし、かつhttp://example.jp/
で始まるすべてのURIに対して同じHTTPクッキーを付与するという例である。
なお、これらの制限は、リクエストメッセージ中のCookie
ヘッダには含まれない。
HTTPクッキーを削除したい場合は、空のクッキーを送信したり、有効期限を過去に設定したクッキーを送信する。
現実のWebクライアントではクッキーの数や容量に制限がある。RFC 6265では、Webクライアントを実装する場合には、次の条件を最低限守るべきだとしている。
一つのクッキー(名前と値のペア)の大きさが4096バイト以上扱えること
1ドメインあたりのクッキー数を50以上扱えること
クッキー総数を3000以上扱えること
Basic認証とDigest認証では、いずれも、まず最初に認証なしのHTTPリクエストをサーバに送信し、ステータスコード401 Unauthorized
のHTTPレスポンスを得る。そして、そのレスポンス中に含まれている情報を用いて認証付きのHTTPリクエストを送信する。
Basic認証では、認証のためのユーザIDとパスワードをBase64エンコーディングした文字列をリクエストヘッダに含める。
例を示そう。まず認証なしのHTTPリクエストをサーバexample.jp
に送信する。
するとexample.jp
は、このURIにアクセスするにはBasic認証が必要であるという情報をWWW-Authenticate
ヘッダにつけて、401 Unauthorized
のHTTPレスポンスを返す。realm
にはWebサーバ上でこの認証につけられた名前が記されている。
このHTTPレスポンスを受け取ったWebクライアントは、ユーザに、認証に必要となるユーザ名とパスワードを入力するよう促す。
ユーザ名とパスワードを受け取ると、Webクライアントはこれらを:
で連結してBase64エンコードし、Authorization
ヘッダに格納して再度HTTPリクエストを行う。以下の例では、taro:pass
(ユーザ名taro
、パスワードpass
)をBase64エンコードした文字列dGFybzpwYXNzCg==
が格納されている。
このリクエストを受け取ったWebサーバは、dGFybzpwYXNzCg==
をBase64デコードしてユーザ名とパスワードを取得し、ユーザ認証を行う。
多くのWebクライアントでは、ユーザによって入力されたユーザ名とパスワードを内部で保存しており、2回目以降のHTTPリクエストでは、ユーザからの入力なしにAuthorization
ヘッダを付与することが多い。
Basic認証では、誰でも容易に復号可能な状態で認証情報(ユーザ名とパスワード)がネットワーク上を流れるという問題点がある。したがって、TLSによる通信路の暗号化を併用することが必須となる。
Digest認証では、ユーザ名やパスワードから一方向ハッシュ関数で計算された値(文字列)をHTTPリクエストに含めるため、Basic認証よりはセキュアな認証方式であると言える。
Digest認証でも、最初はまず認証なしのHTTPリクエストを送信し、401 Unauthorized
のHTTPレスポンスを得る。例は次のようになる。
nonce
はタイムスタンプなどからサーバが生成する文字列であり、Webクライアントが認証情報を組み立てるときに用いられる。opaque
もサーバが生成する文字列であるが、これはそのままHTTPリクエストに埋め込まれ、クライアントの同一性を保証する目的で用いられる。
Basic認証と同様に、これを受け取ると、Webクライアントはユーザにユーザ名とパスワードの入力を求め、それらを元に認証付きのHTTPリクエストを組み立て、サーバに送信する。この際、ユーザから入力されたユーザ名とパスワードのほか、WWW-Authenticate
ヘッダに含まれていたrealm
とnonce
、およびクライアントが生成する文字列cnonce
などからハッシュ値を計算し、それをresponse
に格納する。例は次のようになる。
これを受け取ったサーバは、同様にハッシュ値を計算し、結果がresponse
と一致していれば認証成功となる。
現代のWebアプリケーションで最もよく用いられている認証方式は、HTTPクッキーを用いるものである。
この認証方式では、通常、Webサーバが認証のためのフォームを含んだWebページ(ここではhttp://example.jp/login
としよう)を用意しておく。
ここまで何度か見てきたように、このフォームにユーザがユーザ名とパスワードを入力してログインボタンをクリックすると、次のHTTPリクエストがサーバに送信される。
WebサーバはHTTPリクエスト中のPOSTパラメータの値を元にユーザ認証を行う。認証が成功すると、Webサーバはこのログインに対して固有の文字列(セッションID)を生成し、これをHTTPクッキーとしてWebブラウザに返す。同時に、生成したセッションIDをユーザ名と紐づけてWebサーバ上でも保存しておく(セッションIDテーブル)。
HTTPクッキーの機能通り、Webブラウザは以降のexample.jp
との接続においてHTTPクッキーSID=31d4d96e407aad42
をHTTPリクエストに付与して送信する。
Webサーバexample.jp
では、このHTTPクッキーを基にセッションIDテーブルを検索し、どのユーザからのリクエストかを判断する。サービスからユーザをログアウトさせたい場合は、対応するセッションIDをセッションIDテーブルから削除するとともに、WebブラウザのHTTPクッキーを削除する。
ログアウト機能がないこと
ログインせずに同じページのコンテンツを閲覧するという使い方(ゲストアクセス)ができないこと
サーバ側からログイン状態を無効にする手段が存在しないこと
ホストをまたがったシングルサインオンが実現できないこと
JWTは3つのBase64エンコードされたデータを . で繋げた形の文字列である。
1つ目はJWTで用いられている暗号方式などを含んだJSONデータであり、JWTヘッダと呼ばれる。上の例では、以下のJSONデータ(JWT形式のデータをHMAC-SHA256暗号方式で処理した、という意味)をBase64エンコードしている。
2つ目は認証情報などを含んだJSONデータをBase64エンコードしたもので、JWTクレームセットと呼ばれる。上の例では、以下のJSONデータ(ユーザjoe
をhttp://example.com/is_root
で認証した、有効期限は1300819380
)をBase64エンコードしている。
3つ目はJWTヘッダとJWTクレームセットを . でつないだ文字列に、JWTヘッダで指定されたアルゴリズムを使って行った電子署名である。上の例では、以下の文字列に対してHMAC-SHA256で電子署名を行なっている。
JWTを用いた認証の仕組みを述べる。まずクライアントは、ユーザIDとパスワードをサーバに何らかの方法で送信し、サーバはこれらの情報を元にユーザ認証を行う。この部分はHTTPクッキーの場合と同じである。
認証に成功すると、サーバは(認証に成功した)ユーザIDをJWTクレームセットに含むJWTを生成し、クライアントに返す。
クライアントは返ってきたJWTを保存しておき、以降のリクエストでは、このJWTを(HTTPクッキーと同様)一緒にサーバに送信する。サーバは、送られてきたJWTの電子署名の正当性を検証することで、JWTが改ざんされていないこと、すなわち認証されたユーザからの正当なリクエストであることを確認する。
電子署名の正当性の検証は、JWTに含まれるデータだけで行えるので、HTTPクッキーの場合とは異なり、セッションIDに相当するものをサーバが保存しなくて済む、ということになる。
HTTP/1.1には、Basic認証とDigest認証というユーザ認証方式が規定されている(, )。これらはいずれもHTTPクッキーを使わない。
Digest認証の技術的基盤となっているのは一方向アルゴリズムの一方向性(データからハッシュ値を計算したとき、からを求めることは事実上できない)であるが、もともとDigest認証で用いられていたMD5アルゴリズムにはこの点に関する脆弱性が発見されており、response
からユーザ名とパスワードを取得することがかなり容易に行えると考えられる。したがって、MD5アルゴリズムを用いる場合はDigest認証もBasic認証と同様、通信路の暗号化が必須であると言える。ではよりセキュアな一方向ハッシュ関数であるSHA2がデフォルトとなっている。
この方法でも、最初にフォームからユーザ名とパスワードを送信するときには、リクエストボディ中に平文でパスワードが含まれている。したがってTLSによる通信路の暗号化が必須である。またなんらかの方法でセッションIDを第三者に奪取されると、第三者によるなりすまし攻撃を受ける可能性がある(、CSRF)。
このような弱点があるにも関わらず、Basic認証やDigest認証が使われず、HTTPクッキーを用いた認証が広く使われているのはなぜか。例えばには、次の考察結果を理由として挙げている。
HTTPクッキーによる認証の変形として、JSON Web Token (JWT, )という技術がある。HTTPクッキーとは異なり、サーバがセッションIDに相当するデータを保存しておく必要がない、という点で、最近注目されている技術である。
JWTは、JSON形式で表現されたデータに電子署名をつけるための技術であるJSON Web Signature(JWS, )を元にユーザ認証などの目的で利用しやすくしたもので、技術的基盤は同じである。以下ではJWTの用語を用いて説明を行う。