セッションハイジャックとは、ログイン後に発行されたセッションIDを第三者が手に入れ、パスワードを知らないまま本人としてWebサービスを操作する攻撃です。この記事では、IDが盗まれる5つの経路を2026年時点の手口まで含めて整理し、__Host-付きCookieの設定、漏えいが分かった直後に利用者の全セッションを失効させる実装、Chrome 146で一般提供が始まったDBSC(Device Bound Session Credentials)の使いどころまでを、コード付きで説明します。セッションの発行や保存先の設計そのものは、別記事で扱っています。
まとめ:セッションハイジャックの手口と実装側で先に塞ぐべき箇所
セッションハイジャックの被害は、ほとんどが運営側の実装で防げます。利用者がどれだけ強いパスワードを使っていても、ログイン後のCookieが漏れれば意味がありません。多要素認証を済ませた後のセッションでも同じです。
先に手を付ける順番は次のとおりです。第一にCookieへSecure・HttpOnly・SameSiteを付け、名前を__Host-で始めること。第二にログイン成功時と権限変更時にセッションIDを作り直すこと。第三に、利用者ごとに有効なセッションを索引化し、漏えいやパスワード変更のときに一括で消せる仕組みを持つことです。三つ目は既存の解説記事でほとんど触れられていませんが、事故が起きた日に最も困るのはこの部分です。
DBSCは、端末から抜き取られたCookieを短時間で使えなくする新しい仕組みです。金融・管理画面など被害額が大きいサービスでは検討の価値があります。一般の会員サイトなら、まずCookieの有効期間を短くすることで足ります。
セッションハイジャックの仕組みとパスワード不要でなりすませる理由
セッションIDがログイン済みの証明になるHTTPの構造と攻撃者の狙い
HTTPは1回ごとの通信が独立しているため、Webサービスはログインに成功した利用者へランダムな文字列(セッションID)を渡し、以後のリクエストでその値をCookieとして送り返してもらいます。サーバーは届いたIDを手元の記録と照らし、「この通信はログイン済みの山田さんのもの」と判断します。
つまりログイン後のサーバーは、パスワードではなくセッションIDだけを見ています。攻撃者がこの値を自分のブラウザに入れれば、同じ利用者として扱われます。2段階認証はログインの瞬間を守る仕組みで、その後に発行されたIDまでは守りません。発行から失効までの基本設計はセッション管理とは?セッションIDの発行から失効までの実装と保存先選定にまとめています。
IPAの3分類にみる推測・盗用・固定化とMITRE ATT&CKでの位置付け
IPAの安全なウェブサイトの作り方「1.4 セッション管理の不備」は、攻撃を3つに分けています。単純な規則で作られたIDを当てる「推測」、ログイン中の利用者のIDを取る「盗用」、攻撃者が用意したIDで利用者にログインさせる「固定化」です。狭い意味のセッションハイジャックは盗用を指しますが、結果はどれも同じなりすましになります。
攻撃側の視点では、MITREのATT&CK T1539「Steal Web Session Cookie」が、Cookieはディスク上・ブラウザのメモリ上・通信の途中のどこにでも存在し得ると整理しています。盗んだCookieでログインする段階は、T1550.004という別の技術番号です。対策を考えるときは「どこで盗まれるか」と「盗まれた値がいつまで使えるか」の2軸で見ると抜けが減ります。
セッションIDが奪われる5つの経路と2026年に主流となった盗み方
平文HTTPや公衆Wi-Fiでの盗聴とSecure属性・HSTSで閉じる範囲
最も古い経路は、暗号化されていない通信からCookieを読み取る盗聴です。サイト全体をHTTPSにしていても、CookieにSecure属性が無ければ、利用者が一度http://で開いた瞬間にIDが平文で流れます。IPAの根本的解決4-(iii)が、HTTPS通信で使うCookieにsecure属性を付けるよう求めているのはこのためです。
あわせてHSTS(Strict-Transport-Securityヘッダー)を返し、ブラウザが最初からHTTPSで接続するようにします。この2つが揃えば、ネットワーク上の盗聴でIDを取られる余地はほぼなくなります。
ページにスクリプトを埋め込まれるXSS(クロスサイトスクリプティング)があると、document.cookieを読んで外部へ送る処理を仕込まれます。HttpOnly属性を付けたCookieはスクリプトから読めないため、ID自体の持ち出しは止まります。
ただし止まるのは持ち出しだけです。XSSが残っていれば、攻撃者は利用者のブラウザの中で直接リクエストを送り、送金や設定変更を実行できます。HttpOnlyは被害を減らす層であり、XSSの修正の代わりにはなりません。XSSそのものの塞ぎ方はXSSとCSRFの違いとは?仕組み・被害・対策を比較し、塞ぐ順番まで解説で、外部スクリプトの読み込みを制限する方法はコンテンツセキュリティポリシー(CSP)とは?nonce方式の設定で扱っています。
セッション固定化攻撃の手順とログイン時のID再生成で断つ条件
固定化では、攻撃者がまず自分でサイトにアクセスしてセッションIDを取得し、そのIDを罠のURLなどで利用者のブラウザに持たせます。利用者がそのままログインすると、IDが変わらない実装では攻撃者の手元のIDもログイン済みになります。
断ち方は1つで、ログイン成功の直後に新しいIDを発行し、古いIDを無効にすることです(IPAの4-(iv)-a)。OWASPのSession Management Cheat Sheetは、パスワード変更や管理者権限への切り替えなど、権限が変わるたびに作り直すよう求めています。さらに「自分で発行していないIDを受け付けない」ことも明記しています。
URL・Referer・ログ経由の漏えいとサーバー側からのID流出
IDをURLのパラメータに入れると、リンク先へ送られるRefererやアクセスログ、ブラウザの履歴からIDが漏れます。IPAの4-(ii)はURLに入れないよう求めています。
見落とされがちなのが、サーバー側のデータベースからの流出です。2026年9月に公表されたGyazoの不正アクセスでは、運営会社Helpfeelの発表によると、約2,362万件の利用者情報の中にログインセッションIDとX連携用のトークンが含まれていました。同社は無効化などの対策を実施済みとしています。経緯と利用者側の点検はGyazoの不正アクセスと2,362万件の流出で解説しました。この経路は、Cookie属性をどれだけ整えても防げません。漏れた後に一斉に無効化できるかどうかで被害の大きさが決まります。
インフォスティーラーとAiTMフィッシングが多要素認証を素通りする理由
2026年に最も被害が目立つのは、利用者の端末やフィッシングの中継サーバーからCookieを直接抜く手口です。LummaC2のようなインフォスティーラー(情報窃取型マルウェア)は、ブラウザが保存したCookieファイルをまとめて持ち出します。Evilginx2などのAiTM(中間者型)フィッシングは、本物のログイン画面を中継し、利用者が多要素認証を済ませた直後に発行されたCookieを横取りします。
どちらもログイン手続きの後でCookieを取るため、ワンタイムパスワードやSMS認証では止まりません。サーバー側でできるのは、Cookieの有効期間を短くすること、不審な使われ方を検知して失効させること、端末に結び付けて持ち出しても使えなくすることの3つです。フィッシングに強い認証方式の選び方は多要素認証(MFA)とは?3要素と実装方式・耐フィッシングMFAを参照してください。
Set-Cookieの属性設定とExpressで組むセッションCookieの実装例
__Host-とSecure・HttpOnly・SameSiteのCookie設定値
OWASPのチートシートが示すセッションCookieの推奨形は、__Host-で始まる名前にSecure・HttpOnly・SameSite=Strict・Path=/を付け、Domainを付けない形です。MDNのSet-Cookieの説明のとおり、__Host-で始まるCookieは、この3条件を満たさないとブラウザが保存を拒否します。サブドメインからの上書きを防げる点が利点です。
Node.jsのexpress-sessionで記述した実装例が、次のコードです。外部サイトからのリンクで来た利用者もログイン状態のまま迎えたいならsameSiteは"lax"、管理画面のように外部リンク経由で開く必要が無いなら"strict"にします。OWASPはブラウザの既定値に頼らず、どちらかを明示するよう求めています。
const session = require("express-session");
app.set("trust proxy", 1); // ロードバランサーでTLSを終端する構成のとき
app.use(session({
name: "__Host-sid", // 既定名 connect.sid から変える
secret: process.env.SESSION_SECRET,
store, // Redis などの外部ストア
resave: false,
saveUninitialized: false, // ログイン前は発行しない
cookie: {
httpOnly: true,
secure: true,
sameSite: "lax",
path: "/",
maxAge: 8 * 60 * 60 * 1000 // 絶対期限 8時間
}
}));
設定が効いているかは、ログイン応答のヘッダーを見れば確かめられます。Set-Cookieの行にSecure・HttpOnly・SameSiteが並び、Domainが無ければ想定どおりです。
curl -s -o /dev/null -D - -X POST https://example.com/login \
-d "email=test&password=..." | grep -i "^set-cookie"
IPアドレスやUser-Agentの照合を補助の検知に留めるべき理由
盗まれたIDを見分けるために、ログイン時のIPアドレスやUser-Agentと照合する実装を見かけます。OWASPは照合を勧めつつ、それだけでは信頼できる防御にならないと書いています。企業のプロキシや携帯回線では多くの利用者が同じIPを共有し、User-Agentは自由に書き換えられるからです。
逆に、正規の利用者でもWi-Fiと4G/5Gを行き来するだけでIPは変わります。IPが変わった瞬間に強制ログアウトさせると問い合わせが増えるだけです。実務では、国をまたぐ急な移動など明らかに不自然な変化だけを検知し、再認証を求める形に留めるのが現実的です。
漏えいを検知した直後に全セッションを失効させるRedis実装の手順
利用者ごとにセッションIDを索引化して一括破棄できる構造の作り方
OWASPは「Cookieを削除しただけでは、盗まれたIDの写しは無効にならない」と明記しています。失効はサーバー側で行う必要があります。ところが多くの実装は、セッションをIDで引けても、利用者IDから「その人の有効なセッション一覧」を引けません。これでは「この利用者を全端末からログアウトさせる」ができず、事故が起きた日にストア全体を消すしかなくなります。
次のコードは、ログイン時にIDを作り直したうえで、利用者ごとのRedisのセットにIDを登録し、一括で破棄する関数を用意する例です(node-redis 4系以降の書き方)。
app.post("/login", async (req, res, next) => {
const user = await verifyPassword(req.body.email, req.body.password);
if (!user) return res.status(401).end();
req.session.regenerate(async (err) => { // 固定化対策:IDを作り直す
if (err) return next(err);
req.session.userId = user.id;
await redis.sAdd(`user_sessions:${user.id}`, req.sessionID);
res.redirect("/");
});
});
// 漏えい・パスワード変更・アカウント停止のときに呼ぶ
async function revokeAllSessions(userId) {
const key = `user_sessions:${userId}`;
const sids = await redis.sMembers(key);
await Promise.all(sids.map((sid) =>
new Promise((resolve) => store.destroy(sid, () => resolve()))
));
await redis.del(key);
return sids.length; // 失効させた件数を監査ログに残す
}
ログアウトやタイムアウトで消えたIDはセットに残りますが、store.destroyは存在しないIDでも失敗しないため実害はありません。気になる場合は定期処理でセットを掃除します。
パスワード変更・権限変更・漏えい公表時に失効させる対象と順番
失効させる場面と範囲は、事前に決めておかないと当日に揉めます。目安は次のとおりです。
| きっかけ | 失効させる範囲 | 補足 |
|---|---|---|
| 利用者がパスワードを変更 | 操作中の端末以外の全セッション | 変更した端末はIDを作り直して継続 |
| 管理者権限の付与・剥奪 | 対象者の全セッション | 権限の残ったセッションを残さない |
| 不審なログインを検知 | 対象者の全セッション | 再ログイン時に追加認証を求める |
| セッションストアやDBの流出 | 全利用者の全セッション | 署名鍵・連携トークンも同時に作り直す |
全利用者を一斉にログアウトさせる場面では、セッションの署名に使うsecretを差し替える方法もあります。ただし外部サービスとの連携トークンは別に保存されていることが多く、そちらの失効を忘れがちです。Gyazoの例でも、セッションIDとあわせてX連携用のトークンが流出項目に挙がっていました。
生のセッションIDをDB・ログに残さずハッシュで保存する設計
セッションストアやログに生のIDが残っていると、そのデータが漏れた時点で全員のIDがそのまま使えてしまいます。OWASPは、ログにはIDそのものではなくソルト付きのハッシュ値を記録するよう勧めています。
ストアのキーにもハッシュを使い、Cookieで受け取ったIDをハッシュしてから照合する設計にすれば、ストアが漏れても手元のハッシュからCookieを作り直すことはできません。express-sessionの標準ストアはIDをそのままキーにするため、この設計にするにはストアを自作するか、ハッシュ化に対応した認証ライブラリを選ぶ必要があります。新規に認証基盤を作るなら、最初から入れておく価値があります。
Chrome 146のDBSCで端末に縛るセッションと導入を見送る場面
DBSCの登録・更新エンドポイントとSecure-Session系ヘッダーの流れ
DBSCは、インフォスティーラーで抜かれたCookieを使えなくするための仕組みです。Googleの2026年4月9日の発表によると、Chrome 146でWindows向けに一般提供が始まり、macOSは今後のリリースで対応予定とされています。ブラウザはTPMなどのハードウェアで端末外に出せない鍵を作り、その鍵を持っていることを証明できたときだけ、短命のCookieを受け取れます。
Chromeの開発者向け解説に載っている流れは次のとおりです。仕様本体はW3CのDBSC仕様で公開されています。
# 1. ログイン成功の応答で登録を促す
HTTP/1.1 200 OK
Secure-Session-Registration: (ES256 RS256); path="/StartSession"
# 2. Chromeが公開鍵入りのJWTを /StartSession へ送り、
# サーバーはセッション設定のJSONと短命Cookieを返す
Set-Cookie: auth_cookie=short_lived_grant; Max-Age=600; Secure; SameSite=Lax
# 3. Cookieの期限が来ると、Chromeが更新を要求
POST /RefreshEndpoint HTTP/1.1
Sec-Secure-Session-Id: session_id
# 4. サーバーはチャレンジを返し、Chromeが秘密鍵で署名した応答を送る
HTTP/1.1 403 Forbidden
Secure-Session-Challenge: "challenge_value"
(次のリクエストで Secure-Session-Response: <署名付きJWT>)
Cookieが10分で切れても、正規の端末では裏で更新されるため利用者は気付きません。持ち出された側には秘密鍵が無いため、10分を過ぎると更新できなくなります。
DBSCを今すぐ入れる対象と、Cookieの有効期間短縮で足りる対象
DBSCを入れる価値が高いのは、送金・決済・社内システムの管理画面など、一度なりすまされると被害額が大きく、利用者の多くがWindows版Chromeを使うサービスです。社員向けの業務システムで端末とブラウザを指定できるなら、効果を見込みやすくなります。
反対に、利用者の多くがスマートフォンやSafariから来る一般向け会員サイトでは、2026年11月時点で対応していないブラウザが大半です。登録と更新のエンドポイントを作っても守れる範囲が限られるため、この段階では見送ります。代わりに絶対期限を8時間程度に縮め、重要な操作の前に再認証を求めるほうが費用に見合います。
既存システムでハイジャック対策を点検する順番と外部に任せる判断
既存のWebサービスで最初に確かめるセッション設定項目と点検の順番
手元のサービスを点検するなら、手間の少ない順に次の項目を確かめます。
- ログイン応答の
Set-CookieにSecure・HttpOnly・SameSiteがあるか(前の章のcurlで確認) - セッションIDがURLに出ていないか、アクセスログに記録されていないか
- ログイン前後でセッションIDが変わるか(ブラウザの開発者ツールで値を比べる)
- ログアウト後に古いCookieを戻して送ると、サーバーが拒否するか
- 管理画面から特定の利用者を全端末でログアウトさせる手段があるか
1〜4は半日あれば確認できます。5が無い場合は、前の章の索引化の仕組みを足す改修が必要になります。
認証基盤を作り直すべき条件とフレームワーク標準の設定で足りる条件
Laravel・Rails・Spring Securityなど主要なフレームワークは、ログイン時のID再生成とCookie属性の設定を標準で備えています。点検の1〜4で問題が無く、利用者が1つのサービスに閉じているなら、フレームワークの設定を整えるだけで足ります。
作り直しを考えるべきなのは、複数のサービスで同じログインを使い回している場合、IDを生のまま保存している場合、全セッションの一括失効ができない場合です。この3つが重なると、どこか1か所の漏えいが全サービスのなりすましにつながります。シングルサインオンや失効の仕組みを含めて認証部分を切り出す設計は、認証基盤・ID管理システム開発で相談を受け付けています。
よくある質問
セッションハイジャックについて、検索でよく調べられている疑問に答えます。
セッションハイジャックとセッション固定化の違いは何ですか?
セッションハイジャックは、利用者がすでに持っているセッションIDを盗んでなりすます攻撃です。セッション固定化は、攻撃者が先にIDを用意して利用者に使わせ、利用者がそのIDでログインするのを待つという手順を取ります。結果はどちらもなりすましですが、固定化はログイン時にIDを作り直せば防げるのに対し、盗用は経路ごとに別の対策が必要です。
HTTPSにしていればセッションハイジャックは防げますか?
防げるのは通信途中の盗聴だけです。XSSによる持ち出し、インフォスティーラーによる端末からの窃取、AiTMフィッシング、サーバー側のデータ流出には効きません。HTTPSに加えて、CookieのSecure属性とHSTSを設定しないと、HTTPで開いた瞬間に平文で流れる隙も残ります。
二段階認証を設定していても乗っ取られるのはなぜですか?
二段階認証はログインの瞬間を守る仕組みで、その後に発行されたセッションCookieは守りません。AiTMフィッシングは、利用者が二段階認証を通した直後のCookieを中継サーバーで横取りします。パスキーのように偽サイトでは使えない認証方式と、Cookieの有効期間の短縮を組み合わせると被害を減らせます。
セッションハイジャックされたかどうかはどうやって確認できますか?
利用者側では、サービスのログイン履歴や「ログイン中の端末」の一覧に覚えの無い端末や地域が無いかを確認します。運営側では、同じセッションIDが短時間に離れた国から使われていないか、存在しないIDが大量に送られていないかが監視の対象です。疑わしい場合は全端末からログアウトさせ、パスワードを変更します。
JWTを使えばセッションハイジャックの心配はなくなりますか?
なくなりません。JWTも盗まれれば有効期限まで本人として使えます。しかもサーバー側に状態を持たない設計では、途中で失効させるための手段を別途用意することが必要です。ブラウザで使う場合は、localStorageに置かずHttpOnlyのCookieに入れることが、OWASPの推奨でもあります。
関連記事
- セッション管理とは?セッションIDの発行から失効までの実装と保存先選定を解説:IDの生成条件・保存先・タイムアウトの設計と、PHP・Rails・Springの再生成API。
- XSSとCSRFの違いとは?仕組み・被害・対策を比較し、塞ぐ順番まで解説:Cookieを持ち出される原因になるXSSの塞ぎ方。
- JWTとは?構造・署名検証の仕組みとセッション・OAuth/OIDCとの違いを実装視点で解説:サーバー側セッションとトークン方式の失効のしやすさの比較。
- Gyazoの不正アクセスと2,362万件の流出|第三報の原因と利用者の点検手順:セッションIDと連携トークンがサーバー側から流出した事例。
- WebAuthnとは?パスキー認証の仕組みと登録・認証フローをコードで解説:AiTMフィッシングで盗まれにくいログイン方式の実装。