• 診断項目 No.35/127
  • Cookie・セッション管理
  • 重大度 重要(high)

CookieのSecure属性とは?仕組み・設定方法(PHP・Apache・nginx・IIS・Next.js)・診断基準

CookieのSecure属性は、CookieをHTTPSの通信でだけ送信させる属性です。付け忘れによるセッションハイジャックのリスク、HSTSとの関係、PHP・Apache・nginx・IIS・Next.jsでの設定例、確認方法と診断基準を解説します。

結論(CookieのSecure属性)

CookieのSecure属性とは、Set-Cookieヘッダーに付ける属性で、そのCookieをHTTPS(暗号化された通信)のリクエストにだけ送信させるものです。付いていないCookieは、利用者がhttp://のURLを開いたときやページ内にhttp://の画像が混じっていたときに平文で送られ、公衆無線LANなどでセッションIDを盗まれてログイン状態を乗っ取られるおそれがあります。OWASP ASVS 5.0.0 V3.3.1とIPAが付与を求めています。

この記事のポイント

  • Secure付きCookieはhttps:のリクエストにだけ送られ、HTTPの接続からは設定もできません(rfc6265bis)。
  • HTTPSで配信していても、http://へのリクエストが一度でも起きればSecureなしCookieは平文で送信されます。
  • HSTSとSecure属性は役割が異なるため、両方設定するのが基本です。
  • 診断ではトップと同一オリジンの最大4ページのSet-Cookieを集め、1件でもSecureなしなら×(重み3)です。
  • 設定はアプリケーション側(PHP・ASP.NET・Next.js等)を優先し、Webサーバーでの書き換えは補助策とします。
診断項目名CookieのSecure属性
項目IDcookie.secure
分類Cookie・セッション管理
重大度・配点の重み重要(high) 重み 3
関連する標準・ガイドラインOWASP ASVS 5.0.0 V3.3.1(Level 1)/OWASP WSTG-SESS-02(Testing for Cookies Attributes)/IPA「安全なウェブサイトの作り方」1.4 セッション管理の不備 4-(iii)/draft-ietf-httpbis-rfc6265bis(Cookie仕様改訂版)/CWE-614(Sensitive Cookie in HTTPS Session Without 'Secure' Attribute)

CookieのSecure属性とは

CookieのSecure属性とは、サーバーが Set-Cookie ヘッダーで発行するCookieに付ける属性で、そのCookieをブラウザが HTTPS(暗号化された通信)のリクエストにだけ 送信するよう指示するものです。

Set-Cookie: PHPSESSID=…; path=/; secure; HttpOnly; SameSite=Lax

Secure属性が付いていないCookieは、利用者がうっかり http:// のURLを開いた場合や、ページ内に http:// の画像が1つ混じっていた場合などに、暗号化されない通信でも送信されてしまいます。公衆無線LANなど盗聴可能な経路では、そのCookie(特にセッションID)が第三者に読み取られ、ログイン状態を乗っ取られるおそれがあります。

OWASP ASVS 5.0.0 は V3.3.1(Level 1)で「Cookieに Secure 属性が設定されていること」を求め、IPAも「HTTPS通信で利用するCookieにはsecure属性を加える」ことをセッション管理の根本的解決(4-(iii))としています。

Secure属性の仕組みとブラウザの動作

Secure付きCookieはHTTPSでしか送られない

  • Secure 付きCookieは https: スキームのリクエストにだけ送られます(MDNによればlocalhostは例外)。
  • 改訂版Cookie仕様(rfc6265bis)では、安全でない接続(http)から Secure 付きCookieを設定することはできず、ブラウザはそのCookieを無視します。つまりHTTPで配信しているサイトでは、Secure を付けると逆にCookieが機能しません。Secure属性はHTTPS化が前提の属性です。
  • 属性名は大文字小文字を区別しません(Secure、secure は同じ)。値を伴う書き方(Secure=true)でも属性として扱われますが、標準的な書き方は値なしの Secure です。

HSTSとSecure属性の関係

HSTS(Strict-Transport-Security)を設定していれば、ブラウザは以後 http:// へのアクセスを自動的に https:// に置き換えるため、Cookieが平文で送られる機会は大きく減ります。しかし、HSTSは初回アクセス前やmax-age切れの状態を守れないこと、サブドメインの扱いなどの抜けがあることから、Cookie側の Secure 属性と 両方 設定するのが基本です。HSTSの有無は「HSTSが設定されている」で確認します。

Cookie接頭辞(__Secure-)との関係

ASVS V3.3.1 は、__Host- 接頭辞を使わない場合は __Secure- 接頭辞を使うことも求めています。__Secure- で始まる名前のCookieは Secure が無いとブラウザに拒否されるため、属性の付け忘れを仕組みで防げます。接頭辞の要件違反は「Cookie接頭辞の要件」で評価します。

Secure属性の付け忘れが起きやすい場所

  • PHPの session.cookie_secure が既定値(無効)のまま運用されている
  • ロードバランサー・WAF・CDNが独自に発行するCookie(アプリ側の設定が及ばない)
  • 古い申請システムや外部委託のサブシステムが同じドメインで動いている

CookieのSecure属性についてよくある誤解

  • 「サイト全体がHTTPSなので不要」:HTTPSで配信していても、http:// へのリクエストが一度でも発生すればSecureなしCookieは平文で送信されます。
  • 「セッションCookie以外は関係ない」:ロードバランサーの振り分けCookie(例:AWSALB、BIGipServer〜)にも、内部情報やセッション固定に使われ得る値が含まれることがあります。原則としてすべてのCookieに付与します。
  • 「JavaScriptで設定したCookieも診断される」:この項目が見るのはサーバーの Set-Cookie ヘッダーだけです。document.cookie で設定されるアクセス解析Cookie等は観測対象外です。

CookieのSecure属性の診断基準(セキュリティAIチェッカー)

取得方法

  1. トップページ(転送後の最終応答)に加え、トップページ内の同一オリジンリンクから最大4ページ(ログイン・管理・マイページ系のURLを優先、次にGETフォームの送信先、HTML内の /api/ パス、その他のリンクの順)を GETのみ で巡回します。ログアウト・削除系のURL、画像・PDF等の静的ファイルは除外します。
  2. 各ページの最終応答に含まれる Set-Cookie ヘッダーをすべて収集します(Headers.getSetCookie() で1件ずつ分解)。転送途中の応答(301/302)に付いた Set-Cookie は観測しません。
  3. 削除用のSet-Cookie(Max-Age=0 以下、または過去の Expires)はブラウザに保存されないため判定から除外します。Max-Age と Expires の両方がある場合は仕様どおり Max-Age を優先します。
  4. 残ったCookieが0件なら、この項目を含むCookie属性5項目を「対象外(na)」とします。Cookieが無いことを「安全」とはみなしません。

判定条件

判定条件得点への影響
○ 問題なし(pass)観測した有効なCookieすべてに Secure 属性がある重み3 × 1.00
× 要改善(fail)Secure 属性のないCookieが1件以上ある重み3 × 0
対象外(na)有効なCookieを観測できなかった(結果には重大度high・重み3のまま記録)得点計算から除外
△ 要確認(warn)使用しません―
  • Secure の検出は「; の後に属性名 Secure があり、その直後が ;・=・行末のいずれか」という条件で行い、SecureFlag=1 のような別名の属性を誤って Secure とみなさないようにしています。
  • 根拠欄には確認したCookie件数を表示します(Cookieの値は表示しません)。
  • 対象外の場合は、ログイン・予約などCookieを発行する応答を手動で確認するよう案内します。

自治体サイトでの判定例

観測したSet-Cookie判定
PHPSESSID=…; path=/; secure; HttpOnly; SameSite=Lax○
AWSALB=…; Expires=…; Path=/(Secureなし)×
CMSのログアウトで wp-settings=deleted; expires=Thu, 01-Jan-1970 … のみ対象外(削除用のため除外)
トップと下層でCookieが発行されない静的サイト対象外

Secure属性がないとどんなリスクがあるか

  • セッションハイジャック:無線LANの盗聴、改ざんされたルーター、プロキシなどの経路上で、平文で送信されたセッションIDを取得し、本人になりすましてログインした状態で操作されます(CWE-614)。2010年に公開されたFirefox拡張「Firesheep」は、公衆無線LAN上で平文のCookieを拾うだけで主要SNSのアカウントにログインできることを示し、HTTPS全面化とSecure属性の重要性が広く認識されるきっかけになりました。
  • 強制的な平文通信の誘発:攻撃者が経路上で http:// へのリクエスト(画像タグなど)を差し込めば、SecureなしCookieを送信させることができます。ページ内の http:// 参照は「Mixed Contentがない」で確認します。
  • 自治体での想定被害:電子申請、施設予約、職員向け管理画面などのセッションが乗っ取られると、個人情報の閲覧・改ざん、なりすまし申請、ページ改ざんにつながります。

CookieのSecure属性の設定方法(PHP・Apache・nginx・IIS・Next.js)

PHP

<?php
// php.ini または .user.ini
// session.cookie_secure = 1
// session.cookie_httponly = 1
// session.cookie_samesite = "Lax"

// コードで設定する場合(PHP 7.3以降)
session_set_cookie_params([
    'lifetime' => 0,
    'path'     => '/',
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);
session_start();

setcookie('pref', 'large', [
    'expires'  => time() + 60 * 60 * 24 * 30,
    'path'     => '/',
    'secure'   => true,
    'httponly' => true,
    'samesite' => 'Lax',
]);

Apache(mod_headers:アプリを改修できない場合の補助策)

<IfModule mod_headers.c>
  # Secureが無いSet-CookieにだけSecureを追加(大文字小文字を区別しない)
  Header always edit Set-Cookie "(?i)^(?!.*;\s*secure\s*(?:[;=]|$))(.*)$" "$1; Secure"
  Header edit Set-Cookie "(?i)^(?!.*;\s*secure\s*(?:[;=]|$))(.*)$" "$1; Secure"
</IfModule>

Apacheは応答ヘッダーを2つの内部表で管理しており、CGI・FastCGI(mod_proxy_fcgi)由来の Set-Cookie は always 側、それ以外の多くは既定(onsuccess)側に入るため、両方の条件で記述します。正規表現での書き換えは、既存の属性と二重になったり、HTTPで配信するパスでCookieが機能しなくなったりするため、テスト環境で確認してから適用してください。

nginx(リバースプロキシ)

# nginx 1.19.3以降
proxy_cookie_flags ~ secure httponly samesite=lax;

IIS(ASP.NET)

<system.web>
  <httpCookies requireSSL="true" httpOnlyCookies="true" />
  <sessionState cookieSameSite="Lax" />
</system.web>

Next.js(Vercel)

import { cookies } from "next/headers";

(await cookies()).set("session", token, {
  secure: true,
  httpOnly: true,
  sameSite: "lax",
  path: "/",
});

段階的な導入と注意点

  1. サイト全体がHTTPSで動作し、http:// で利用されるページ(古い申請フォーム等)が残っていないことを確認します。HTTPのページが残っているとSecure付きCookieは送信・設定されず、ログインできなくなります。
  2. HTTP→HTTPSの恒久リダイレクト(「HTTPからHTTPSへ転送」)とHSTSを先に整えます。
  3. アプリケーション(PHP、CMS、フレームワーク)の設定でSecureを付与します。Webサーバーでの書き換えは補助策です。
  4. 確認します。
curl -sSI https://www.city.example.lg.jp/login/ | grep -i '^set-cookie'

ブラウザの開発者ツール(Application → Cookies)でも「Secure」列にチェックが付いていることを確認します。修正後はセキュリティAIチェッカーで再診断し、根拠欄が「すべてSecureです」になることを確かめます。

なお、JavaScriptからの読み取りを防ぐ属性は「CookieのHttpOnly属性」、クロスサイト送信の制御は「CookieのSameSite属性」の各ページで解説しています。

よくある質問(FAQ)

サイト全体がHTTPSなら、Secure属性は不要ではありませんか?

不要ではありません。HTTPSで配信していても、利用者がhttp://のURLを開いたり、攻撃者が経路上でhttp://への画像リクエストを差し込んだりすれば、SecureなしのCookieは平文で送信されます。

HSTSを設定していればSecure属性は要りませんか?

HSTSは初回アクセス前やmax-age切れの状態を守れず、サブドメインの扱いにも抜けが生じ得ます。Cookie側のSecure属性とHSTSの両方を設定するのが基本です。

HTTPのページが残っているサイトでSecureを付けるとどうなりますか?

改訂版Cookie仕様では、安全でない接続からSecure付きCookieを設定できず、HTTPのページにはSecure付きCookieが送られません。そのページではログインできなくなるため、先にサイト全体をHTTPS化し、HTTP→HTTPSの恒久リダイレクトとHSTSを整えてください。

PHPでSecure属性を付けるにはどうすればよいですか?

php.iniまたは.user.iniで session.cookie_secure = 1 を設定します。コードで設定する場合はPHP 7.3以降の session_set_cookie_params や setcookie の配列オプションで 'secure' => true を指定します。

JavaScriptで設定しているアクセス解析のCookieも診断されますか?

されません。この項目が観測するのはサーバーのSet-Cookieヘッダーだけで、document.cookieで設定されるCookieは対象外です。

Cookieを発行していないサイトはどう判定されますか?

有効なCookieを観測できなかった場合は「対象外」とし、得点計算から除外します。Cookieが無いことを「安全」とはみなさず、ログインや予約などCookieを発行する画面は手動で確認してください。

セキュリティAIチェッカーを利用する

この項目を含む127項目を、URLを入れるだけで無料診断します。

参考資料