はじめに
Chrome DevToolsでCookieを確認すると、認証トークンやセッションIDらしき値がCookieに保存されていることがあります。
Cookieに認証情報を持たせること自体は珍しくありません。むしろ、サーバー側セッションの識別子をCookieに保存する方式は一般的です。
一方で、認証に関係するCookieでは「Cookieに保存しているか」よりも、HttpOnly、Secure、SameSiteなどの属性が適切に設定されているかが重要です。
この記事では、DevToolsで認証Cookieを見つけたときに確認したいポイントを整理します。実際のシステム名、ホスト名、Cookie名、トークン値は使用せず、一般化した例で説明します。
結論
認証Cookieを確認するときは、まず次の3属性を見ます。
| 属性 | 確認したい状態 | 主な目的 |
|---|---|---|
HttpOnly |
JavaScriptから読む必要がなければ有効 | XSS時のCookie窃取リスクを下げる |
Secure |
HTTPS運用なら有効 | HTTP通信でCookieが送信されることを防ぐ |
SameSite |
要件に合わせて明示 | クロスサイトリクエスト時のCookie送信を制御する |
典型的なセッションCookieであれば、たとえば次のような設定が候補になります。
Set-Cookie: SESSION_ID=...; Path=/; HttpOnly; Secure; SameSite=Lax
ただし、常にこの設定が正解というわけではありません。外部サイトから戻ってくる認証フローや、JavaScriptが明示的にトークンを読む設計では要件が変わります。
Cookieに認証情報を保存すること自体は問題なのか
問題ではありません。
たとえばサーバー側でセッションを管理する構成では、ブラウザにはセッションを識別するためのランダムなIDだけをCookieとして保存し、実際のログイン状態はサーバー側に保持します。
Browser
Cookie: SESSION_ID=abc123
|
v
Server
abc123 -> userId=100, authenticated=true
この方式では、ブラウザが対象ドメインへHTTPリクエストを送るたびにCookieを自動的に送信できます。
したがって「認証情報がCookieにあるから危険」という判断は適切ではありません。
見るべきなのは、値の性質とCookie属性です。
バックエンドでセッション管理している場合
PHPの$_SESSIONのように、ログイン状態やAPIトークンなどをバックエンド側のセッションに保存する構成でも、ブラウザ側には通常、そのセッションを識別するためのCookieがあります。
Browser
Cookie: PHPSESSID=abc123
|
v
Backend
abc123
├─ userId
├─ userName
└─ apiToken
この場合、ブラウザが知る必要があるのは基本的にセッションIDだけです。フロントエンドJavaScriptがセッションIDそのものを読む必要がないのであれば、セッションCookieにはHttpOnly、Secure、SameSiteを適切に設定します。
また、バックエンドのセッション内にAPIトークンを保持しており、そのトークンをバックエンドから利用できるのであれば、同じAPIトークンを別Cookieとしてブラウザへ渡す必要がない可能性があります。
たとえば次のような状態です。
Backend Session
apiToken = xyz789
Browser Cookie
API_TOKEN = xyz789
この場合は、API_TOKENのCookie属性を確認するだけでなく、そもそもAPIトークンそのものをCookieとしてブラウザへ保存する必要があるのかを確認することが重要です。
不要な認証情報をブラウザへ持たせなければ、その分だけ漏えいする対象も減らせます。
HttpOnly=falseで何が問題になるのか
HttpOnlyを付けると、JavaScriptからそのCookieを取得できなくなります。
Set-Cookie: SESSION_ID=abc123; HttpOnly
HttpOnlyが付いていないCookieは、条件によっては次のようにJavaScriptから参照できます。
console.log(document.cookie)
認証用のセッションIDやアクセストークンをJavaScriptから読む必要がないのであれば、HttpOnlyを付ける方が安全です。
そもそもXSSとは
XSS(Cross-Site Scripting)は、Webページ上で攻撃者が用意したJavaScriptなどのスクリプトが、正規サイトの一部として実行されてしまう脆弱性です。
たとえば、ユーザーの入力値を適切にエスケープせず、そのままHTMLとして表示すると、次のようなコードが実行される可能性があります。
<script>
// 攻撃者が実行させたい処理
</script>
XSSが成立すると、攻撃者はそのページを閲覧しているユーザーの権限で、画面を書き換えたり、リクエストを送信したり、JavaScriptから参照可能な情報を取得したりできる可能性があります。
CookieがJavaScriptから参照可能な場合、認証情報の窃取につながることもあります。
XSSが起きたときの差
HttpOnlyがない状態でXSSが成立すると、攻撃者がページ上で任意のJavaScriptを実行し、JavaScriptから参照可能なCookieを取得できる可能性があります。
XSS発生
↓
攻撃者のJavaScriptが実行される
↓
document.cookieを参照
↓
認証Cookieが外部へ送信される
HttpOnlyを設定すれば、JavaScriptからの直接的なCookie読み取りを防げます。
ただし、HttpOnlyはXSSそのものを防ぐ機能ではありません。XSSが成立すれば、Cookieの値そのものを読めなくても、ユーザー権限で画面操作やリクエスト送信を行われる可能性は残ります。
そのため、HttpOnlyはXSS対策の代わりではなく、被害を限定するための防御の一つと考えるのが適切です。
参考: MDN - Set-Cookie / HttpOnly
Secure=falseで何が問題になるのか
Secureを付けたCookieは、原則としてHTTPS通信でのみ送信されます。
Set-Cookie: SESSION_ID=abc123; Secure
認証CookieにSecureが付いていない場合、HTTPでアクセスできる経路が存在すると、その通信にもCookieが送信される可能性があります。
そのため、HTTPSを前提とする本番Webアプリケーションでは、認証CookieにSecureを設定するのが基本です。
一方で、ローカル開発環境や検証環境ではHTTPSを利用していないため、意図的にSecureを外している場合があります。
なお、ブラウザによってはlocalhostをSecure CookieのHTTPS要件の例外として扱う場合があります。したがって、ローカル開発だから必ずSecure=falseにしなければならない、というわけではありません。
DevToolsでSecure=falseを見つけた場合は、すぐに脆弱性と断定するのではなく、次を確認します。
- 本番環境でも同じ設定か
- HTTPでアクセスできる経路が存在するか
- HTTPSへ強制リダイレクトしているか
- HSTSを利用しているか
SameSite=unspecifiedは問題なのか
SameSiteは、クロスサイトのリクエストでCookieを送信するかを制御する属性です。
代表的な値は次の3つです。
| 値 | 概要 |
|---|---|
Strict |
クロスサイトからのCookie送信を強く制限する |
Lax |
一部のトップレベル遷移などを許可する |
None |
クロスサイトでも送信する。Secureが必要 |
たとえば次のように明示できます。
Set-Cookie: SESSION_ID=abc123; SameSite=Lax
現在の主要ブラウザでは、SameSiteを省略した場合にLax相当として扱うことがあります。
ただし、ブラウザがデフォルトで適用するLax相当の挙動は、明示的なSameSite=Laxと完全に同じとは限りません。ブラウザによっては、Cookie設定直後の一定時間に限り、通常のLaxより緩い条件でクロスサイトのPOSTリクエストにもCookieを送信する場合があります。
そのため、認証Cookieではブラウザのデフォルト挙動に依存させず、アプリケーションの要件を確認した上でSameSiteを明示しておく方が意図を読み取りやすくなります。
また、SameSiteはCSRFリスクを下げるための有効な仕組みですが、これだけを唯一のCSRF対策として扱うべきではありません。アプリケーションの構成によってはCSRFトークンなども検討します。
参考: MDN - Set-Cookie / SameSite
セッションIDとAPIトークンでは扱いが違う
DevTools上では、次のような認証関連Cookieを見かけることがあります。
SESSION_ID
API_TOKEN
この2つは同じように見えても、設計上の役割が異なる可能性があります。
SESSION_ID
サーバー側セッションを識別するだけなら、フロントエンドJavaScriptが値を知る必要は通常ありません。
この場合はHttpOnlyと相性が良いです。
Set-Cookie: SESSION_ID=...; HttpOnly; Secure; SameSite=Lax
API_TOKEN
フロントエンドがトークンを取得し、Authorizationヘッダーへ設定する設計の場合は話が変わります。
fetch('/api/example', {
headers: {
Authorization: `Bearer ${token}`,
},
})
この設計ではJavaScriptがトークン値を読む必要があるため、Cookieへ保存したままHttpOnlyを付けることはできません。
その場合は単純に「HttpOnly=falseだから修正する」のではなく、次を確認します。
- なぜJavaScriptからトークンを読む必要があるのか
- Cookieをブラウザに自動送信させる方式へ変更できないか
- トークンの有効期限は十分短いか
- トークン漏えい時に失効できるか
- XSS対策が適切に行われているか
属性だけでなく認証方式全体を見る必要があります。
DevToolsで見つけたときの確認手順
Cookie一覧で認証情報らしい値を見つけたら、次の順番で確認すると整理しやすいです。
1. このCookieは何のための値か
↓
2. そもそもブラウザへ保存する必要があるか
↓
3. JavaScriptから読む必要があるか
↓
4. HttpOnlyは必要か
↓
5. HTTPS専用にできるか
↓
6. Secureは付いているか
↓
7. クロスサイトで送信する必要があるか
↓
8. SameSiteの値は要件と一致しているか
特に重要なのは、DevToolsの表示だけを見て脆弱性と断定しないことです。
Cookie属性は、アプリケーションの認証フローや通信経路とセットで評価します。
また、認証情報そのものをCookieとして保存している場合は、属性だけを見るのではなく「なぜブラウザへその値を持たせているのか」まで確認します。
DomainとPathも確認する
認証CookieではDomainとPathも確認します。
Set-Cookie: SESSION_ID=...; Domain=example.com; Path=/
Cookieの送信範囲を必要以上に広くすると、本来Cookieを必要としないURLやサブドメインまで影響範囲が広がります。
可能であれば、Cookieの利用範囲は必要最小限にします。
なお、PathはCookieを送信するURLを制御する仕組みであり、認証情報を保護するセキュリティ境界として扱うものではありません。
より厳格にするなら__Host-プレフィックスも検討する
認証用Cookieでは、Cookie名に__Host-プレフィックスを付ける方法もあります。
Set-Cookie: __Host-SESSION_ID=...; Path=/; HttpOnly; Secure; SameSite=Lax
__Host-で始まるCookieは、対応ブラウザでは次の制約を満たす必要があります。
-
Secureが必要 -
Domainを指定できない -
Path=/が必要
Domainを指定できないため、そのCookieを発行したホストだけが対象になります。
すべての認証Cookieで必須というわけではありませんが、ホスト単位でセッションCookieを安全に扱いたい場合には有力な選択肢です。
よくある誤解
Cookieにトークンを入れること自体が危険
Cookieという保存先だけでは判断できません。
HttpOnlyなCookieとして保持する方式は、JavaScriptからトークンを扱わなくて済むという利点があります。
HttpOnlyを付ければXSS対策は完了する
HttpOnlyはCookieの読み取りを制限する属性です。
XSS自体を防ぐものではありません。
SameSiteを付ければCSRF対策はすべて不要
SameSiteは強力な防御ですが、認証方式やブラウザ要件によっては追加のCSRF対策が必要です。
開発環境でSecure=falseだから脆弱性である
ローカルや検証環境ではHTTPSを使用していない場合があります。
重要なのは本番環境でどのような設定になっているかです。
まとめ
認証情報がCookieに保存されていること自体は問題ではありません。
DevToolsで認証Cookieを確認したときは、Cookieの保存有無ではなく、次の観点を確認します。
- そもそもその値をブラウザへ保存する必要があるか
- JavaScriptから読む必要がなければ
HttpOnlyを付ける - HTTPS運用なら
Secureを付ける - 要件に合わせて
SameSiteを明示する -
DomainとPathを必要以上に広げない - 必要に応じて
__Host-プレフィックスを検討する - セッションIDとAPIトークンの役割を区別する
- バックエンドセッションを利用しているなら、認証情報を別Cookieとして重複して持たせる必要があるか確認する
- 属性だけでなく認証方式全体で判断する
DevToolsでHttpOnly=false、Secure=false、SameSite=unspecifiedを見つけた場合、それだけで即座に脆弱性とは断定できません。しかし、認証Cookieであれば「なぜその設定なのか」を確認する十分な理由になります。