トピックを検索
ログインし直すと入力内容が消えてしまうセッション切れ
保険金の請求フォームは、最後のリクエストから30分でログアウトさせることをどこにも書いていないうえ、ログインし直すと、入力した内容が消えた状態のフォームに戻ってしまう
res.redirect('/sign-in') 問題
フォームの入力に時間がかかる人が、制限があることを知らないままログアウトされ、ログインし直すと、書いた内容をすべて失う
保険会社の請求フォームには、証券番号と、事故の状況を自分の言葉で書く欄があります。台所の天井からの水漏れを説明するには時間がかかります。日付を調べたり、修理業者の領収書を探したり、途中で一息ついたりします。この保険会社は、セキュリティのため、サーバーへの最後のリクエストから30分で利用者をログアウトさせます。入力してもサーバーには何も送られないため、文章を書いている間も、30分の残り時間は減っていきます。制限があることは、フォームのどこにも書かれていません。40分後、利用者は「送信する」を押します。サーバーはセッションがないと分かると、送られた内容を捨ててログイン画面へリダイレクトします。画面には「セッションの有効期限が切れました。もう一度ログインしてください」と出ています。ログインし直すと請求フォームに戻りますが、フォームは空になっています。
これは、よく使われるフレームワークの既定の動きです。RailsのDevise(ログイン用のライブラリー)も、Laravelも、ログインし直した後に元のページへ戻しますが、覚えているのはページのアドレスだけで、そこへ送られた内容は覚えていません。Djangoも、ログイン画面へリダイレクトするときに残すのは next のパスだけです。Laravelでは、フォームのセキュリティトークンもセッションと一緒に切れるため、同じボタンを押すと、ログイン画面ではなく「419 Page Expired」の画面が出ることもよくあります。
達成基準2.2.5「再認証」は、ログイン中のセッションが切れても、ログインし直した後に、データを失わずに作業を続けられることを求めています。制限そのものは認められています。2.2.5が求めているのはセッションを長くすることではなく、切れたときに入力した内容が失われないことです。W3Cの失敗例F12は、まさにこの形を挙げています。セッションが切れてログインし直すと、フォームに入力した内容が入っていないため、制限より長い時間が必要な人は、いつまでも入力を終えられません。
達成基準2.2.6「タイムアウト」は、どれだけ操作しないでいるとデータが失われるかを、利用者に知らせることを求めています。データを20時間より長く保存する場合は対象外です。W3Cは、この案内を作業の最初に一度出すよう求めています。そうすれば、先に書類をそろえるか、休憩を取るかを利用者が決められます。このフォームは何も書いておらず、何も残さないため、昼休みを取るだけで入力した内容がすべて失われます。どちらも、ブラウザーが原因ではありません。ページは普通のフォームを送っているだけで、セッションがなくなっていたときに入力内容を捨てるのは、サーバーの判断です。
ある人には10分で書ける説明に、別の人は1時間かかることがあります。時間がかかる人ほど失うものも大きく、苦労して書いた部分をもう一度書くか、請求そのものをあきらめることになります。スマートフォンでは、写真や保険証券を探しに別のアプリへ切り替えている間も、サーバーには何も送られないため、その時間も制限に数えられます。
10グループ中3グループに影響
- 全盲 (影響あり)
- 弱視 (影響なし)
- 色覚 (影響なし)
- ろう (影響なし)
- 難聴 (影響なし)
- 発話 (影響なし)
- 手の操作 (影響あり)
- 筋力・可動域 (影響なし)
- 認知 (影響あり)
- 光過敏 (影響なし)
- 言語・認知・学習の能力が限られる 読み書きに時間がかかる人、時間を忘れがちな人、領収書を探しに席を外した人は、制限があると知らないまま時間切れになります。ログインし直すとフォームは空で、考えて書いた文章はもう残っていません。
- 手の操作が限られる スイッチ、視線入力、スクリーンキーボード、音声入力での入力には時間がかかります。長い説明を書いていると、サーバーへ一度もリクエストを送らないまま、すぐに30分を超えます。
- 視覚を使わない スクリーンリーダーの利用者は、長いフォームを進むのにも、書いた文章を読み返すのにも時間がかかります。ログインし直した後は、入力欄を一つずつ読んで、フォームが空になったことを知ります。
制限はサーバーの設定で、リダイレクトが起きるのは時間切れの後に送られたフォームだけです。スキャンには、ちゃんと動くフォームに見えます。スキャンはログインして30分待ってから送信したりはせず、送った内容をサーバーが残すかどうかはページのどこにも現れないため、EaseWebの診断にはこの失敗を見つけるルールがありません。実際に時間切れまで待った人だけが気づきます。フォームをログインしないと使えないようにしてほしいとAIアシスタントに頼むと、フレームワークそのままのリダイレクトを書くことがあります。これは下の修正前の例と同じで、セッションの制限を加えるときも、フォームの内容をどうするかには触れないことがあります。
試してみる
30分離れてから請求を送り、もう一度ログインしてみましょう
ウィンドウの上の時計は、離れている時間を進めるためのデモ用のもので、保険会社のサイトの一部ではありません。フォームを離れてよい時間は書かれていますか?ログインし直すと、請求の内容はどうなっていますか?
修正を適用して、同じことを試してください。
問題あり 修正できますか?
修正方法
ログインし直しても、入力した内容を残す
<form id="claim" method="post" action="/claims"> <h1>保険金の請求</h1> <label for="policy">証券番号</label> <input id="policy" name="policy" value="{{ fields.policy }}"> <label for="what">事故の状況</label> <textarea id="what" name="what" rows="8">{{ fields.what }}</textarea> <button>送信する</button></form>問題点4点
- 30分でログアウトされることも、そのとき入力した内容が失われることも、フォームのどこにも書かれていません。
- 入力してもサーバーには何も送られないため、30分書き続けても、30分離れていたのと同じに数えられます。
- その後に送った請求は捨てられ、ログイン後のために残るのはフォームのアドレスだけです。
- ログインし直すと、フォームは空で戻ってきます。
ページの上でログインし直し、同じ請求を送る
<form id="claim" method="post" action="/claims"> <h1>保険金の請求</h1> <label for="policy">証券番号</label> <input id="policy" name="policy" value="{{ fields.policy }}"> <label for="what">事故の状況</label> <textarea id="what" name="what" rows="8">{{ fields.what }}</textarea> <button>送信する</button></form> <dialog id="sign-in-again" aria-labelledby="sign-in-title"> <form id="sign-in" method="post" action="/sign-in"> <h2 id="sign-in-title">もう一度ログインして、請求を送信してください</h2> <p>入力した請求の内容は残っています。パスワードを入力すると送信します。</p> <input type="hidden" name="username" value="{{ user.email }}" autocomplete="username"> <label for="password">パスワード</label> <input id="password" name="password" type="password" autocomplete="current-password"> <button>ログインして送信</button> </form></dialog>修正の手順4ステップ
- 請求は
fetchでページから送ります。サーバーがどう答えても、フォームはそのまま残ります。 - セッションが切れていたら、リダイレクトではなく401で請求に答えます。これで、ページの側でログインを求めればよいと分かります。
- フォームの上に、ログインのダイアログを
showModal()で開きます。フォーカスはダイアログへ移ります。アカウントは入力済みなので、求めるのはパスワードだけで、パスワード管理ツールでも入力できます。 - ログインできたらダイアログを閉じ、同じ請求をもう一度送ります。
これで修正前の例は直り、2.2.5と2.2.6を満たします。請求の内容はページに残り続けるため、どれだけ離れていても失われません。失われるのはタブを閉じたときだけで、W3Cはタブを閉じることをタイムアウトとして数えていません。ただし、スクリプトが必要で、ログインもページの中で済ませられなければなりません。別のサイトを通してログインするサイトは、次の修正か、下のシングルサインオンの項を参考にしてください。
ページの上でログインし直す方法と、送られた内容を残す方法は、どちらも2.2.5と2.2.6を満たします。どちらが合うかは、フォームの作りで決まります。フォームをすでにスクリプトで送っているか、サイトがアプリなら、ダイアログのほうが変更は小さく、利用者はページを離れず、どこまで進んでいたかも失いません。スクリプトを使わない普通のHTMLのフォームの場合や、ログインが別のサイトを通るためダイアログに出せない場合は、サーバーで送られた内容を残します。スクリプトは要りませんが、利用者は読み込み直したページに戻ります。案内だけでは足りません。保存してはならない入力欄のために、ほかの二つのどちらかと組み合わせて使います。
セキュリティトークン。 多くのフレームワークは、クロスサイトリクエストフォージェリを防ぐフォームのトークンをセッションに結び付けています。そのため、セッションが切れた後に送られたフォームは、ここに挙げたコードが動く前に、そのチェックで止まります。Laravelの「419 Page Expired」の画面がこれです。このときも、セッションがない場合と同じように、入力内容を残すか、ページからログインを求めるようにします。ページの上でログインし直すとセッションは新しくなるため、ログインの応答で新しいトークンを返し、それを請求に入れてから送り直します。
誰のデータか。 制限があるのは、共用のパソコンや、席を外したままのパソコンのためです。残した内容が次の人に渡ってはいけません。戻すのは入力したアカウントにだけとし、フォームに戻したら削除します。ダイアログはアカウントを入力済みにしてパスワードだけを求めるので、ほかの人がそこから請求を続けることはできません。
シングルサインオン。 会社のIDプロバイダーなど、別のサイトを通してログインし直す場合は、ページ自体がそのサイトへ移ってから戻ってくるため、ダイアログではフォームを持ち越せません。サーバーで送られた内容を残すか、ログインをポップアップウィンドウで開き、フォームはその後ろに残します。
タブの中のコピー。 サーバーを変えられない場合は、入力に合わせてスクリプトでフォームを sessionStorage にコピーし、ログイン後に同じタブでフォームが読み込まれたときに戻す方法もあります。コピーはタブがある間だけ残り、タブを閉じれば消えます。アカウントも一緒に保存し、同じアカウントのときだけフォームに戻します。
ファイル。 ページからファイルの入力欄の値を設定することはできず、スクリプトが files で戻せるのも、自分で保存しておいたファイルだけです。そのため、請求のために選んだ写真は、読み込み直すと消えてしまいます。添付ファイルは選ばれた時点でアップロードして請求と一緒に一覧にするか、ページの上でログインし直す方法を使います。その方法なら、選んだファイルは入力欄に残ります。ただし、ファイルを送れない URLSearchParams ではなく、FormData のまま送信します。
長い休みのための下書き。 入力に合わせて下書きをアカウントに保存し、20時間より長く残せば、さらに確実です。タブを閉じても、ブラウザーが落ちても、別の端末に替えても残り、20時間より長く残すデータについては、2.2.6は案内を求めていません。ただし、セッションが切れた後に入力した分は、ログインし直すまで保存できないため、ほかの修正のどちらかと組み合わせます。W3Cは、こうしたデータを残す前に、プライバシーに関する規制が同意を求める場合があると注意しています。健康や支払いの情報を含むフォームでは、プライバシーと法務の担当者に相談してください。
残してはならないもの。 PCI DSSは、カード番号とセキュリティコードの保存を厳しく制限しているため、どちらもこの方法では残しません。ログインし直した後はその欄を空のまま表示して、入力し直す欄を示します。その欄の制限は、案内のようにフォームの最初に伝えます。
修正にならないもの。 30分を60分にするなど、セッションを長くしても、それより長く離れた人の請求は失われます。終了の前に警告し、ログインを続けられるダイアログは2.2.1が求めるもので、別のガイド警告も延長もないセッションタイムアウトで扱っています。警告を見逃した人はフォームを失うため、それだけでは2.2.5を満たしません。
修正の確認
5項目、マウス不要
確認方法: キーボード、スクリーンリーダー、パスワード管理ツール
OS、ブラウザーと支援技術のバージョン、ビルド、実施日、各手順の実際の結果を記録してください。セッションの設定、ログインの流れ、フォームを変更した際は、再度確認します。
スクリーンリーダーでの <dialog>の読まれ方
制限
このガイドで扱うのは、セッションが切れたときに入力した内容が失われることと、それまでの時間を伝えることです。制限の前に警告し、延長できるようにするのはレベルAで、別のガイド警告も延長もないセッションタイムアウトで扱っています。時間制限そのものがある作業は2.2.3「タイミング非依存」の対象で、別のガイド確保した座席が時間切れで解放されてしまう購入手続きのタイマーで扱っています。2.2.5と2.2.6はAAAで、W3Cはサイト全体にAAAを求めることを勧めていません。それでも、ログインが必要な長いフォームなら、入力した内容を残すだけでどちらも満たせます。入力内容を残して安全かどうか、どれだけの間残すかは、サービスを運営する人が、プライバシーの担当者と一緒に決めることです。
よくある問題を再現した学習用のサンプルです。実在のクライアントの診断結果ではありません。コードは出発点となる実装例のため、実際の製品でも確認してください。ここでは支援技術でのテスト結果は報告していません。
EaseWebのサービス
修正した問題を、次のリリースで再発させない
このガイドのような修正方法を、コーディングエージェントが従うルールと、CIのチェックに組み込みます。あわせて定期的に、キーボードとスクリーンリーダーで手動の再テストを行います。