验证 Referer 头是防护 CSRF 攻击的一种传统方法,通过检查 HTTP 请求的 Referer 头来验证请求来源的合法性。
Referer 头的基本原理
Referer 头是 HTTP 请求头的一部分,它包含了发起请求的页面 URL。服务器可以通过检查 Referer 头来判断请求是否来自受信任的来源。
验证 Referer 头的实现
基本验证逻辑
javascriptfunction validateReferer(req, trustedDomains) { const referer = req.headers.referer; if (!referer) { return false; // 拒绝没有 Referer 的请求 } try { const refererUrl = new URL(referer); const refererDomain = refererUrl.hostname; return trustedDomains.some(domain => refererDomain === domain || refererDomain.endsWith('.' + domain) ); } catch (error) { return false; // 无效的 URL } } // 使用示例 app.post('/api/transfer', (req, res) => { const trustedDomains = ['example.com', 'www.example.com']; if (!validateReferer(req, trustedDomains)) { return res.status(403).send('Invalid referer'); } // 处理转账请求 });
验证策略
1. 严格匹配
- Referer 必须完全匹配受信任的域名
- 提供最强的安全性
- 可能影响某些合法请求
2. 域名匹配
- 允许子域名
- 更灵活的验证
- 需要确保所有子域名都是受信任的
3. 允许空 Referer
- 某些情况下 Referer 可能为空(如直接输入 URL)
- 需要结合其他验证方式
- 降低安全性
Referer 头的局限性
1. 隐私设置
- 某些浏览器或隐私插件可能阻止发送 Referer
- 用户可以禁用 Referer 头
- 导致合法请求被拒绝
2. 可伪造性
- Referer 头可以被攻击者伪造
- 不是绝对安全的防护方式
- 需要配合其他防护措施
3. HTTPS 降级
- 从 HTTPS 页面发起 HTTP 请求时,Referer 可能被移除
- 混合内容场景下的处理复杂
4. 浏览器兼容性
- 不同浏览器对 Referer 的处理可能不同
- 某些移动浏览器的行为不一致
最佳实践
1. 多层防护
javascriptfunction csrfProtection(req, res, next) { // 验证 Referer if (!validateReferer(req, trustedDomains)) { return res.status(403).send('Invalid referer'); } // 验证 CSRF Token if (req.body._csrf !== req.session.csrfToken) { return res.status(403).send('Invalid CSRF token'); } next(); }
2. 配置 Referer-Policy
html<meta name="referrer" content="strict-origin-when-cross-origin">
3. 白名单管理
- 维护受信任的域名列表
- 定期更新和审查
- 支持动态配置
4. 日志记录
javascriptif (!validateReferer(req, trustedDomains)) { logger.warn('Invalid referer attempt', { referer: req.headers.referer, ip: req.ip, path: req.path }); return res.status(403).send('Invalid referer'); }
适用场景
适合使用 Referer 验证的场景
- 内部管理系统
- API 接口保护
- 作为辅助防护措施
不适合单独使用的场景
- 公共网站
- 对安全性要求极高的系统
- 需要支持多种访问方式的场景
现代替代方案
随着 SameSite Cookie 和 CSRF Token 的普及,Referer 验证通常作为辅助防护手段使用,而不是主要的防护方式。
验证 Referer 头虽然有一定的局限性,但在正确的使用场景下,配合其他防护措施,仍然可以提供有效的 CSRF 防护。