Outline

Revision as of 2023-08-07 03:46

 
--- parent: ../Security title: Digest認証とセッション認証を組み合わせた認証の提案 date: 2019-11-23 tags: セッション認証, フォーム認証, ダイジェスト認証, Web, PHP --- 本稿では, Digest認証, およびセッション認証の欠点を互いに補うあう, 二つを組み合わせた認証方法を提案する. まず, Digest認証とセッション認証について簡単に説明したのち, 本題に入る. === # Digest認証 Digest認証は, HTTPの認証の一つで, Webサーバはユーザのブラウザと 資格情報(ユーザ名, パスワードなど)をやり取りする^[digest-wiki]. Digest認証は, ユーザ名とパスワードをハッシュ化してから, ネットワークに流す. 一方, 別のHTTP認証であるBasic認証は, ハッシュ化の代わりに簡単に複合可能なBase64変換を使うため, TLSと組み合わせない限りセキュア(安全)ではない. Digest認証の一般的な手続き^[digest-wiki]は以下のとおりである. 1. クライアントは, 認証が必要なページにアクセスする. 2. Webサーバは, 401 "Unauthorized" ステータスとともに, \ `authentication realm`, ランダムな文字列`nonce`を返す. 3. (ブラウザにユーザ名とパスワードの情報が無いとき)\ ブラウザは, ユーザに`authentication realm`を提示し, ユーザ名とパスワードの入力を求める.\ ここで, ユーザはキャンセルできる. 4. クライアントは, レスポンスコードを含めた認証ヘッダを加えて同じリクエストをWebサーバに送る. 5. Webサーバは認証を認め, ページを返す. もしユーザ名, パスワードが異なる場合, \ サーバは401を返し, クライアントは, 再びユーザに入力を求める. ![ダイジェスト認証手続き](CURRENT_DIR/Images/digest.jpg) # 特徴 * 資格情報のやり取りでユーザ名とパスワードはハッシュ化される(平文ではない) * 一度認証されると, ブラウザは自動で認証ヘッダを送信するため, ログアウト機能はない^[digest-logout] # セッション認証 セッションベースの認証は, ユーザのログイン状態をサーバに保存する認証方法の一つである^[session-what]. セッションベースの認証では, ユーザがログインしたときにサーバがセッションデータを作成し保存する. その次にセッションIDをユーザのブラウザにクッキーとして保存する. そのあとの処理で, ブラウザがセッションIDをサーバに送り, サーバはそれとセッションデータを照らし合わせて, ユーザのログイン状態を把握する. セッション認証の一般的な手続き^[session-what]は以下のとおりである. 1. クライアントは, ユーザ名とパスワードを送信 2. サーバはセッションデータを作成し保存後, セッションIDをブラウザのクッキーに送信 3. クライアントはセッションIDを含めて認証が必要なページにアクセス 4. サーバはセッションIDとセッションデータを照らし合わせる 5. セッションデータからユーザがログイン状態であるとき, サーバはページを送信 ![セッション認証手続き](CURRENT_DIR/Images/session.jpg) # 特徴 * ログイン/ログアウト機能がある. ログアウトはセッションの破棄で可能 * ログイン後は, セッションIDでログイン状態を判定するので, \ クライアントは, アクセス権が必要なページごとに資格情報を送る必要なし. * フォーム入力によりユーザ名・パスワードを送信するため, \ クライアント側でユーザ名とパスワードを暗号化していないときや, \ 通信路が暗号化されていない(TLSではない)ときは安全ではない. # 提案手法 本提案手法は, Digest認証とセッション認証の欠点を互いに補い合う方法である. Digest認証およびセッション認証の利点と欠点は次のとおりである. Digest認証: ✅利点: * 資格情報のやり取りでユーザ名とパスワードはハッシュ化される(平文ではない) ❌欠点: * ログアウト機能はない * 認証が必要なページごとに資格情報を送る必要あり セッション認証: ✅利点: * ログイン/ログアウト機能 あり * 認証が必要なページごとに資格情報を送る必要なし ❌欠点: * 単純なフォーム認証の場合, ユーザ名・パスワードが平文でネットワークに流れる 二つの欠点を補いあい, 本提案手法の利点は次のとおりである. 提案手法: ✅利点: * 資格情報のやり取りでユーザ名とパスワードはハッシュ化される(平文ではない) * ログイン/ログアウト機能 あり * 認証が必要なページごとに資格情報を送る必要なし # 本手法が適している状況 本手法が向いていると思われる状況は, * TLSを使用できない環境で, ある程度セキュリティを確保しつつユーザ管理を持ったウェブアプリを作りたい状況^[注.situ] です. フリーのレンタルサーバを使ってTLSを使用できない状況で ちょっと凝ったウェブアプリ・ウェブシステムを作りたい方などに当てはまるかもしれません. # 認証手続き 1. サーバはログインページを送信 2. ユーザがログインURLをクリック 3. サーバは, 401ステータスとともに有効期限付きのnonceとrealmを返す 4. ブラウザは, realmを表示し, ユーザにユーザ名とパスワードの入力を求めたのち, サーバに送信 5. サーバは, ユーザ名とパスワードを検証し, 成功したとき, nonceを破棄し, セッションデータを作成, \ セッションIDをブラウザのクッキーに送信 6. クライアントはセッションIDを含めて認証が必要なページにアクセス 7. サーバはセッションIDとセッションデータを照らし合わせる 8. セッションデータからユーザがログイン状態であるとき, サーバはページを送信 ![Digest認証とセッション認証を組み合わせた認証手続き. \ セッション認証のユーザ名パスワードの送信部分にDigest認証の方法が用いられている](CURRENT_DIR/Images/digest-session.jpg) # digest認証でのログアウトについて 一般的に, digest認証では認証状態(ログイン・ログアウト状態)をブラウザ側が持っています^[digest-wiki]^[digest-logout]. なので, サーバ側がログアウトをブラウザに強制することはできません. そこで, 先行手法では, 「ダミーのユーザ名で再認証を促す^[digest-logout-trad-method]」といった方法があります. ですが, この方法では, 一部のブラウザ(firefoxなど)で警告が表示される場合があり, ユーザに不信感を与えかねません. 本提案方法では, //一度限りの認証のみ有効な制限時間付き//の`nonce`を用意することで対応させています. 本手法では, 認証が必要な時に制限時間付きの`nonce`を発行し, ブラウザに資格情報を送らせます. ブラウザからの応答が遅く制限時間を超えると発行した`nonce`は無効化されます. また, 制限時間内に資格情報を送り検証に成功した場合でも, 発行した`nonce`を無効化します. 一度限りの`nonce`のおかげで, ブラウザに再認証を促すことが可能です. 多くのブラウザでは, `nonce`を使いまわして再認証する手間を省きますが, 本手法ではそれができません. ブラウザは認証ページで必ずサーバから401ステータスをもらって再びユーザにユーザ名とパスワードを入力してもらう必要があります^[注.logout-safari]. 認証が必要なページごとに再認証が求められるわけではなく, 求められるのはログイン時のみです. 本手法では一度認証に成功(ログイン)すると, それ以降はdigest認証ではなくセッションベースの認証を行います. ログアウトでは, セッションベースの認証と同じくセッションの破棄を行います. # 実装 本手法「Digest認証とセッション認証を組み合わせた認証方法」は, 2020-8-20時点で [ContentsPlanet](https://contentsviewer.work/Master/ContentsPlanet/ContentsPlanet)というコンテンツ管理システム(CMS) で使用されています. ContentsPlanet は, TLSを利用できない環境を想定して作られています. 本手法が実装されたスクリプトは以下のようになっています. `Authenticator.php`: <https://github.com/ContentsViewer/ContentsPlanet/blob/master/Module/Authenticator.php> * digest認証での検証処理 * セッションベース認証での検証処理 `CacheManager.php`: <https://github.com/ContentsViewer/ContentsPlanet/blob/master/Module/CacheManager.php> * 一時的に`nonce`を記録したりする `index.php`: <https://github.com/ContentsViewer/ContentsPlanet/blob/master/index.php> * フロントページの窓口 * ここで, 認証が必要か確認する * 再認証が必要な時は, `403ページ`へとぶ `403.php`: <https://github.com/ContentsViewer/ContentsPlanet/blob/master/Frontend/403.php> * 再認証を促すページ * ここに, ログイン画面へのリンクがある `login.php`: <https://github.com/ContentsViewer/ContentsPlanet/blob/master/Frontend/login.php> * ログインページ `logout.php`: <https://github.com/ContentsViewer/ContentsPlanet/blob/master/Frontend/logout.php> * ログアウトページ * セッションの破棄を行う # 注釈 [注.situ]: かなりニッチです [注.logout-safari]: ブラウザであるsafariはそれでも, 再認証をパスします. あきらめずもう一度トライしているのでしょうか? \ ただブラウザを一度終了すると再認証を求められます. # 参考文献 [digest-wiki]: "[Digest access authentication](https://en.wikipedia.org/wiki/Digest_access_authentication)". \ Wikipedia. (accessed at 2020-2-18) [digest-logout]: "[ログアウト機能の目的と実現方法](https://blog.tokumaru.org/2013/02/purpose-and-implementation-of-the-logout-function.html)". \ 徳丸浩の日記. (accessed at 2020-2-19) [session-what]: "[What really is the difference between session and token based authentication](https://dev.to/thecodearcher/what-really-is-the-difference-between-session-and-token-based-authentication-2o39)". \ Brian Iyoha. (accessed at 2020-2-22) [digest-logout-trad-method]: "[PHPによる簡単なログイン認証いろいろ](https://qiita.com/mpyw/items/bb8305ba196f5105be15)". \ Qitta. (accessed at 2020-3-1)
Retrieved from "https://www.contentsviewer.work/Master/Web/Security/DigestSessionAuthentication/article?cmd=history&rev=1691347578"