はじめに
この記事では、クライアントサイドの JavaScript だけでセッションタイムアウトを管理しようとした結果、遅延の考慮漏れに直面した体験をもとに、セッション管理の考え方についてまとめてみました。
なお、今回 setTimeout を使用したのは、認証の有効期限を管理するためではなく、モーダルダイアログ内で別の JavaScript 機能を初期実行させるためでした。このような少し特殊な使い方が、結果として問題を引き起こす原因となったので自戒の意味もこめて残しておきます。
1. セッションタイムアウトとは?
ユーザーがログイン状態のまま長時間操作しなかった場合、自動的にログアウトさせる仕組みです。主な目的は、放置された端末からの不正利用やセッションハイジャックを防ぐことにあります。副次的に、不要なセッション情報を破棄することで サーバー側のリソース消費を抑える効果 もあります。
近年は、セッション情報の保存に外部キャッシュ(例:Redis)を活用する手法が一般的になっており、
クラウドインフラの普及によりメモリ単価も大幅に低下しています。
そのため、リソース節約は以前ほど切実な理由ではなくなりつつありますが、
大規模サービスにおいては依然として無視できない観点です。
2. なぜセッション管理が必要?
ここで言う“セッションタイムアウト”は本来、以下のような “ユーザー認証セッションの有効期限” を指します。
典型ユースケース | 具体例 |
---|---|
Webログイン | SNS や管理画面で無操作◯分 → 再ログイン要求 |
決済情報保護 | EC サイトのカート保持時間、カード入力画面の自動期限切れ |
機密業務アプリ | 社内システムで離席時に自動ロック |
今回は この “認証セッション” ではなく、単にダイアログ初期化のための setTimeout
を使った ——という “ズレ” が問題点です。以下が使用上の効果と具体例です。
効果 | 具体例 |
---|---|
不正利用防止 | 共有 PC で放置 → 第三者が勝手に操作 |
情報漏洩対策 | 退席中に個人データを抜き取られるリスク減 |
サーバー負荷軽減 | 使われていないセッションを掃除してメモリ節約 |
3. クライアント vs サーバー:責任分界点
領域 | 特徴 | 向いている役割 |
---|---|---|
クライアント (JS) | ユーザー操作に依存/停止・改ざんが容易 | UI 表示・再ログイン誘導など“お知らせ係” |
サーバー | コントロール不可・高い信頼性 | 最終アクセス時刻の記録と有効期限判定“審判役” |
原則: 認証状態の真偽はサーバーで決める。
4. よくある setTimeout 実装例
setTimeout(() => {
alert('セッション切れです');
location.href = '/login';
}, 30 * 60 * 1000); // 30 分後
4-1. なぜ危険?
- 改ざんが容易: DevTools で clearTimeout すれば無効化。
- バックグラウンド遅延: 非アクティブタブでタイマー精度が落ち、30 分 → 数時間後に発火。
- サーバーとのズレ: サーバーではタイムアウト済→ API は 401。フロントは「生きてる」つもりで連打→ すべて失敗。
5. 私がハマった実例(ダイアログページ問題)
モーダルダイアログで開いた子ページに 初期化スクリプトを呼び出しするための setTimeout
を設置(認証とは無関係)。
// ダイアログ描画後に関数を走らせたかった
setTimeout(() => {
demoData();
}, 0);
ポイントは 0ms 指定でも実際には描画キューに入るため「一呼吸」遅れること。その結果、Chrome のバックグラウンドタブ最適化などと組み合わさると倍以上遅延し、UI がチラつく or 目的の関数が効かないケースの発生が考えられました。
最終的な解決策: あらかじめ CSSで制御できる範囲だったことから、JS での初期状態の変更自体を不要にした。結果、setTimeout は削除。
6. セッションタイムアウトを“使うべき状況”と最小実装
まず「そもそもセッションタイムアウトが必要なケース」への認識が曖昧だったことが今回の大きな問題点でした。そこで以下を自分の中で使用するときの参考にしていこうと思います。
6‑1. どんなときに導入する?
該当シーン | 代表例 |
---|---|
機密情報を扱う画面 | ユーザーアカウント設定、給与明細、医療情報など |
決済・課金フロー | カード番号入力、振込依頼、アドレス変更直後のトランザクション |
共有端末想定 | ネットカフェ、社内共用 PC、ホテルロビー端末 |
今回のように単なるダイアログ初期化では採用しない──遅延・チラつきの原因になる!
6‑2. サーバー側でタイムアウト判定をする
- PHP/Laravel : config/session.php → lifetime に分数指定。
- Node.js (express-session) : cookie.maxAge と rolling/resave で最後のアクセスを更新。
- 共通ポイント : 「最後のアクセス時刻」を基準にサーバーだけで判定。フロントが細工できない安心設計。
6‑3. 401 エラーで自動リダイレクト
フロントは“通知係”に徹し、API で 401 を受けたら速やかにログインページへ誘導します。
axios.interceptors.response.use(
res => res,
err => {
if (err.response?.status === 401) {
window.location.href = '/login';
}
return Promise.reject(err);
}
);
単一情報源はサーバー。フロントは 401 が来たら従うだけ。
まとめ
JavaScriptのタイマーは、あくまで認証系のサポート役として、UIの補助に使うくらいがちょうどいいとわかりました。またフロントで使う場合には、「セッション切れのお知らせ」や「延命のためのリクエスト」、「401エラーの検知」などにとどめておくのが安心だなということを再確認できました。
「とりあえずJavaScriptでサクッとやろう」と思って実装したら指摘を受けた内容だったので私自身の体験から、気をつけていきたいなという出来事です🐰💦
採用拡大中!
アシストエンジニアリングでは一緒に働くフロントエンド、バックエンドのエンジニア仲間を大募集しています!
少しでも興味ある方は、カジュアル面談からでもぜひお気軽にお話ししましょう!
お問い合わせはこちらから↓
https://official.assisteng.co.jp/contact/