防御点击劫持的方法(如 X-Frame-Options)
点击劫持是一种恶意攻击手段,攻击者通过透明或伪装的iframe覆盖在合法页面上,诱导用户点击看似无害的元素,实际触发隐藏的恶意操作。防御点击劫持的核心方法包括设置HTTP头、JavaScript防护以及现代浏览器提供的安全策略。
X-Frame-Options 的工作原理
X-Frame-Options 是一个HTTP响应头,用于控制页面是否允许被嵌入到 <frame>
、<iframe>
或 <object>
中。它有三个可选值:
- DENY:完全禁止页面被嵌入到任何框架中。
- SAMEORIGIN:仅允许同源页面嵌入。
- ALLOW-FROM uri:允许指定来源的页面嵌入(已废弃,部分浏览器支持)。
# Nginx 配置示例
add_header X-Frame-Options "SAMEORIGIN";
# Apache 配置示例
Header always set X-Frame-Options "DENY"
Content-Security-Policy 的 frame-ancestors 指令
现代浏览器推荐使用 Content-Security-Policy
(CSP) 的 frame-ancestors
指令替代 X-Frame-Options
,因为它提供了更细粒度的控制:
Content-Security-Policy: frame-ancestors 'self' https://trusted.example.com;
此配置仅允许当前域名和 trusted.example.com
嵌入页面。若需完全禁止嵌套,可设置为:
Content-Security-Policy: frame-ancestors 'none';
JavaScript 防御方案
对于不支持HTTP头的旧环境,可通过JavaScript检测页面是否被嵌套:
if (window !== window.top || window.top.location !== window.location) {
window.top.location = window.location;
}
更完善的方案需结合 try-catch
避免沙箱限制:
try {
if (window.self !== window.top) {
document.body.style.visibility = 'hidden';
window.top.location = window.location;
}
} catch (e) {
console.error('Frame busting blocked:', e);
document.body.innerHTML = '此页面不允许被嵌入框架';
}
多层级嵌套与沙箱限制
当页面被嵌套在多层iframe中,或沙箱属性限制了JavaScript执行时,需额外处理:
-
沙箱iframe检测:
if (window.self !== window.top) { try { parent.document; // 尝试访问父文档,沙箱中会抛出异常 } catch (e) { document.body.innerHTML = '<h1>请直接访问此页面</h1>'; } }
-
CSS 视觉干扰防护:
body { display: none !important; } @media all { html { background: url("data:image/png;base64,...") repeat; } }
通过CSS强制隐藏内容,除非页面是顶级窗口。
实际场景中的组合策略
-
关键操作二次确认:
function confirmAction() { if (window.self === window.top) { performPayment(); } else { showModal('请在独立窗口中完成此操作'); } }
-
登录态敏感操作: 结合后端验证
Referer
和Origin
头:# Django 中间件示例 class ClickjackingMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): response = self.get_response(request) if request.path.startswith('/payment/'): response['X-Frame-Options'] = 'DENY' response['Content-Security-Policy'] = "frame-ancestors 'none'" return response
浏览器兼容性与降级方案
-
特性检测:
const supportsCSP = 'Content-Security-Policy' in document.createElement('meta'); if (!supportsCSP) { document.write('<meta http-equiv="X-Frame-Options" content="DENY">'); }
-
服务端动态适配:
app.use((req, res, next) => { const userAgent = req.headers['user-agent']; const isOldBrowser = /MSIE [6-8]|Firefox\/[1-3]/.test(userAgent); res.setHeader(isOldBrowser ? 'X-Frame-Options' : 'Content-Security-Policy', isOldBrowser ? 'DENY' : "frame-ancestors 'none'"); next(); });
框架集成示例
-
React 防护方案: 在根组件中添加防护逻辑:
useEffect(() => { const handleFrameCheck = () => { if (window.self !== window.top) { window.location.href = `${window.location.origin}/frame-warning`; } }; handleFrameCheck(); window.addEventListener('focus', handleFrameCheck); return () => window.removeEventListener('focus', handleFrameCheck); }, []);
-
Vue 指令实现:
Vue.directive('anti-framing', { inserted(el) { if (window.self !== window.top) { el.style.display = 'none'; document.body.innerHTML = '<div class="fullscreen-warning">安全提示:请勿在框架内打开此页面</div>'; } } });
移动端特殊处理
移动端浏览器可能对iframe的处理存在差异,需额外注意:
-
WebView 防护:
// Android WebView 配置 webView.getSettings().setAllowUniversalAccessFromFileURLs(false); webView.getSettings().setAllowFileAccessFromFileURLs(false);
-
iOS 弹窗拦截:
document.addEventListener('click', (e) => { if (window.self !== window.top && e.target.closest('.sensitive')) { e.preventDefault(); window.open(window.location.href, '_blank'); } }, { capture: true });
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn
上一篇:点击劫持的危害
下一篇:使用 CSP 防止点击劫持