WebアプリにログインするたびにCookieが発行されている。そのCookieが盗まれれば、攻撃者はパスワードを知らなくてもログイン済み状態を再現し、システムを自由に操作できてしまう。
セッションハイジャックやCSRF(クロスサイトリクエストフォージェリ)といった攻撃を防ぐ最初の砦が、Cookieの「セキュリティ属性」だ。HttpOnly・Secure・SameSite——3つの属性を正しく設定するだけで、Webアプリのリスクは大幅に下がる。
この記事では、それぞれの属性の意味・仕組み・具体的な設定方法を、現場で使えるレベルで解説します。自社のWebサービスや社内Webシステムを運用している情シス担当者の方は、ぜひ最後まで読んでみてください。

CookieとセキュリティーなぜCookieが攻撃者の標的になるか
Cookieとは、WebサーバーがブラウザにSet-Cookieヘッダーで渡す小さなデータだ。ログイン後の「セッションID」もCookieに格納される。ブラウザはリクエストのたびにCookieを自動送信するため、サーバー側はセッションIDを見て「この人はログイン済みだ」と判断する仕組みになっている。
つまり、セッションIDが書かれたCookieを入手できれば、攻撃者はパスワードを知らなくてもログイン済み状態を再現できる。これがセッションハイジャックだ。
Cookieを盗む主な手口は次の2つだ。
・XSS経由の窃取: 攻撃者が悪意のあるスクリプトをページに埋め込み、document.cookieでCookieの値を読み取って外部サーバーに送信する
・通信の盗聴: HTTPSではなくHTTPで通信しているとき、CookieがプレーンテキストとしてネットワークXに流れ、途中で盗み見られる
これらの攻撃経路を封じるのが、Set-CookieヘッダーのHttpOnly・Secure・SameSite属性だ。それぞれの役割を順番に見ていこう。
HttpOnly属性 — JavaScriptからのCookie窃取を防ぐ
HttpOnlyは、CookieをJavaScriptからアクセスできないようにする属性だ。
Set-CookieヘッダーにHttpOnlyを付与すると、ブラウザはdocument.cookieを通じたCookieの読み書きを禁止する。これにより、ページにXSSの脆弱性があり、悪意のあるスクリプトが埋め込まれてしまったとしても、セッションCookieを読み取って外部に送信することができなくなる。
Set-Cookieヘッダーでの記述例を見てみよう。
# セッションCookieにHttpOnlyを設定する例 Set-Cookie: sessionid=abc123xyz; HttpOnly # 複数の属性を組み合わせた推奨形式 Set-Cookie: sessionid=abc123xyz; HttpOnly; Secure; SameSite=Lax
HttpOnlyで防げること・防げないこと
HttpOnlyはXSS経由のCookie窃取を防ぐが、万能ではない。何が防げて何が防げないかを整理しておこう。
・防げる: XSS攻撃でのdocument.cookie読み取りによるセッションCookie窃取
・防げない: 別ドメインからの偽リクエスト送信(CSRF攻撃)。CookieはHTTPリクエスト時に自動付与されるため、HttpOnlyでは防げない。ここはSameSiteが担う
・防げない: HTTP通信での盗聴。これはSecure属性が担う
・防げない: XSS攻撃そのもの。スクリプトが動けばフォーム改ざんなど他の被害が起きる。入力値エスケープやCSPとの併用が必要だ
セッションCookie(ログイン状態を管理するCookie)には、必ずHttpOnlyを付けておくことを現場の基本ルールとして徹底してほしい。フロントエンドのJavaScriptがどうしてもCookieにアクセスしたい場合は、セッションとは別のCookieを使い、そちらにはHttpOnlyを付けないという使い分けが必要になる。
Secure属性 — HTTPS通信のみにCookieを限定する
Secure属性を付けると、ブラウザはHTTPS(TLS)で暗号化された接続のときだけCookieを送信する。HTTP(平文)接続ではCookieを送らないため、通信経路での盗聴リスクを排除できる。
# Secure属性の設定例 Set-Cookie: sessionid=abc123xyz; Secure # HttpOnlyと組み合わせた最低限の推奨設定 Set-Cookie: sessionid=abc123xyz; HttpOnly; Secure
Secure属性はHTTPSの利用が前提だ。HTTPSに対応していない環境ではSecure付きのCookieはブラウザから送信されなくなるため、ログイン状態が保持されなくなる。2026年現在、常時HTTPS化はWebサービスの当然の対策となっているが、まだ対応できていない古い社内システムも存在する。Secure属性の活用に先立ち、まずHTTPS化を優先しよう。
開発環境でHTTPを使っている場合、Secure属性のせいでCookieが送信されず動作しないことがある。そのような場合は、環境変数で本番環境とステージング・開発環境を切り替え、本番だけSecureを有効にする設計にする。
SameSite属性 — クロスサイトリクエストを3段階で制御する
SameSite属性は、別のドメイン(クロスサイト)からのリクエスト時にCookieを送信するかどうかを制御する。CSRF攻撃への主要な対策として機能する属性だ。
CSRFとは何か、簡単に整理しておこう。ログイン済みのユーザーが悪意のあるサイトを訪問したとき、そのサイトが裏でサービスへの偽リクエストを自動送信する攻撃だ。ブラウザはリクエストにCookieを自動付与するため、サーバー側は正規ユーザーからの操作と区別できなくなる。SameSiteはこの「自動送信」を制限することでCSRFを防ぐ。
SameSiteには3つの値がある。それぞれの挙動と使い所を説明する。
1. SameSite=Strict — 外部サイトからの送信を完全禁止
外部サイトからのすべてのリクエストでCookieを送信しない。セキュリティレベルが最も高い設定だ。
たとえば、ユーザーがメールのリンクをクリックしてサービスにアクセスしたとき、Cookieは送信されないためログアウト状態で表示される。利便性よりセキュリティを優先する場面——金融サービス、社内管理画面、決済システムなど——に向く設定だ。
Set-Cookie: sessionid=abc123xyz; SameSite=Strict; HttpOnly; Secure
2. SameSite=Lax — モダンブラウザのデフォルト値
外部サイトからのナビゲーション(リンクのクリックやリダイレクト)ではCookieを送信するが、iframe内の読み込みやfetch()・XMLHttpRequestによるサブリソース取得では送信しない。
Strictより使い勝手が良く、CSRF対策としても十分なケースが多い。Chrome 80以降、SameSite属性を省略した場合のデフォルトはLaxになっている。一般的なWebサービスにはLaxが推奨設定だ。
Set-Cookie: sessionid=abc123xyz; SameSite=Lax; HttpOnly; Secure
3. SameSite=None; Secure — クロスサイト送信を明示的に許可
外部サイトからのリクエストでもCookieを送信する。サードパーティのiframeを埋め込むサービス(外部決済フォーム、チャットウィジェット、広告配信など)で必要になる設定だ。
ただし、SameSite=Noneを使う場合はSecure属性の付与が必須だ。Chrome 80以降、SecureなしのSameSite=Noneは拒否される。
# SameSite=NoneにはSecureが必須(Secureなしはブラウザに拒否される) Set-Cookie: embed_session=xyz789; SameSite=None; Secure; HttpOnly
具体的な設定方法 — 言語・環境別のコード例
1. PHPでの設定
PHPではsession_set_cookie_params()でセッションCookieの属性を一括設定する。PHP 7.3以降は連想配列形式が使える。
0, // セッション終了まで 'path' => '/', 'domain' => '', 'secure' => true, // Secure: HTTPS専用 'httponly' => true, // HttpOnly: JSからアクセス不可 'samesite' => 'Lax', // SameSite: クロスサイト制限 ]); session_start(); // 任意のCookieを設定する場合(PHP 7.3以降の配列形式) setcookie( 'user_pref', 'dark_mode', [ 'expires' => time() + 86400, 'path' => '/', 'secure' => true, 'httponly' => true, 'samesite' => 'Lax', ] );
2. Node.js(Express + express-session)での設定
const session = require('express-session'); app.use(session({ secret: process.env.SESSION_SECRET, // 環境変数から取得 resave: false, saveUninitialized: false, cookie: { httpOnly: true, // HttpOnly secure: true, // Secure(本番環境はtrue) sameSite: 'lax', // SameSite=Lax maxAge: 1000 * 60 * 60, // 1時間(ミリ秒) }, })); // 開発環境でHTTPを使う場合のみsecure: falseに切り替える // cookie: { secure: process.env.NODE_ENV === 'production' }
3. Pythonの設定(Django)
# settings.py SESSION_COOKIE_HTTPONLY = True # HttpOnly SESSION_COOKIE_SECURE = True # Secure SESSION_COOKIE_SAMESITE = 'Lax' # SameSite CSRF_COOKIE_HTTPONLY = False # CSRFトークンはJSが読む必要があるためFalse CSRF_COOKIE_SECURE = True # CSRFトークンもSecureは付ける CSRF_COOKIE_SAMESITE = 'Lax'
4. 設定の確認方法(curl・開発者ツール)
curlでSet-Cookieヘッダーを確認するには次のコマンドを使う。
# ログインエンドポイントのヘッダーを確認(-I でヘッダーのみ取得) curl -I -X POST https://example.com/login -d "user=test&pass=test" # 正しく設定されているときのSet-Cookieレスポンス例 # Set-Cookie: sessionid=abc123; Path=/; HttpOnly; Secure; SameSite=Lax
Chromeの開発者ツールで確認する方法も覚えておこう。
・F12でDevToolsを開く
・Applicationタブ → 左メニューのCookies → 対象ドメインを選択
・各Cookieの「HttpOnly」「Secure」列にチェックマークが入っているか目視確認する
・「SameSite」列でLax/Strict/Noneのいずれかが表示されているか確認する
セッションCookieのHttpOnlyとSecureにチェックが入っていない、またはSameSiteが空欄の場合は要修正だ。
中小企業でも今日からできること
Webサービスを外部委託しているケースや、パッケージシステムを使っているケースでも、確認・改善できることがある。
・現状確認を最初に: ブラウザのDevToolsで自社サービスにログインし、発行されるCookieのHttpOnly・Secure・SameSiteを確認する。5分でできる作業だ
・開発ベンダーへの依頼: 受託開発や保守委託先に「セッションCookieにHttpOnly・Secure・SameSite=Laxを設定してほしい」と伝える。具体的な属性名を伝えれば技術者に正確に届く
・WordPressの場合: セキュリティプラグイン(Wordfence等)でCookieのセキュリティ設定を強化できる場合がある。また、nginxやApacheの設定でSet-Cookieヘッダーに属性を後付けする方法も存在する
・HTTPS化を先に: まだHTTP環境が残っている場合はHTTPS化が最優先。Let’s Encryptで無償対応できる
・コミュニケーションのコツ: 「OWASP Top 10のA07(認証の失敗)対策として、セッションCookieのセキュリティ属性を整備したい」と伝えると、技術者・ベンダーに意図が正確に伝わる
OWASP Top 10とは?2021年版の全脆弱性リストと各項目の対策も合わせて読むと、Cookie設定がどの脅威カテゴリに対応しているか把握しやすい。
よくある誤解と注意点
【誤解1】「HttpOnlyがあればXSS対策は完了」
HttpOnlyはCookieの窃取を防ぐが、XSS攻撃そのものを止めるわけではない。スクリプトが動いてしまえば、Cookieを盗まなくてもページの内容改ざん、フォームへの不正入力、ユーザーへの偽表示などができる。HttpOnlyはXSS対策の一要素に過ぎず、入力値エスケープやCSPヘッダーとの組み合わせが必須だ。CSPについてはセキュリティヘッダー完全ガイド(CSP・HSTS・X-Frame-Options)で詳しく解説している。
【誤解2】「SameSite=Laxを設定すればCSRFトークンは不要」
SameSite=LaxはCSRFに対して強力な対策になるが、GET リクエスト経由の攻撃(ナビゲーションではCookieが送られるため)には完全ではない。重要な状態変更(パスワード変更・決済処理など)にはCSRFトークンとの併用が望ましい。
【誤解3】「SameSite=Noneはsecureなしでも使える」
Chrome 80以降、SameSite=NoneにSecureが付いていないCookieは自動的に拒否される。外部iframeや決済連携が突然動かなくなった場合、この設定漏れが原因のことが多い。
【誤解4】「Cookieのセキュリティ設定はHttpOnlyだけで十分」
3つの属性はそれぞれ異なる攻撃ベクトルを防ぐ。HttpOnly(XSS経由窃取)・Secure(通信盗聴)・SameSite(CSRF)は役割が別々であり、3点セットで使って初めて多層的な防御になる。
ログイン後のセッションID再生成を忘れずに
Cookieのセキュリティ属性を適切に設定していても、ログイン成功後にセッションIDを再生成しないとセッション固定化攻撃のリスクが残る。ログイン前のセッションIDを引き継がないよう、認証成功時に必ず新しいセッションIDを発行する設計にしよう。詳細はセッション固定化攻撃(Session Fixation)とは?で解説している。

本記事のまとめ
CookieのHttpOnly・Secure・SameSite属性は、Webアプリの認証を守るうえで最も基本的かつ即効性のある設定だ。3点セットで理解しておこう。
| 属性 | 目的 | 防げる主な攻撃 | 推奨設定 |
|---|---|---|---|
| HttpOnly | JSからCookieを隠す | XSS経由のCookie窃取 | セッションCookieに必ず付ける |
| Secure | HTTPS接続のみに限定 | 平文通信での盗聴・改ざん | HTTPS環境で必ず付ける |
| SameSite=Lax | クロスサイトの自動送信を制限 | CSRF攻撃 | 一般的なWebサービスの標準 |
| SameSite=Strict | クロスサイト送信を完全禁止 | CSRF攻撃(最高強度) | 金融・管理画面など高リスク系 |
| SameSite=None | クロスサイト送信を明示許可 | (許可が必要な場合に限定使用) | 必ずSecureと併用すること |
セッションCookieには最低でもHttpOnly; Secure; SameSite=Laxの3点セットを設定する。これが現場の標準だ。まだ設定できていないなら、今日中に自社サービスのCookieをDevToolsで確認してみてほしい。
PR
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版(徳丸浩/SBクリエイティブ)
Cookieのセキュリティ属性を含むWebアプリ全般の脆弱性対策を体系的に習得できる一冊。XSS・CSRF・セッション管理など本記事で触れたテーマをコード例付きで詳解しており、開発者・情シスを問わず手元に置いておきたい国内標準テキストです。
