iframe 安全与沙箱(iframe Security and Sandbox)
一、iframe 基础与安全问题
1.1 基本用法
<iframe src="https://example.com/page.html" width="800" height="600">
您的浏览器不支持 iframe
</iframe>
1.2 安全风险
iframe 引入的主要安全风险:
- 点击劫持:将目标网站嵌入iframe,通过透明层诱导用户点击
- XSS 攻击:如果 iframe 加载的页面存在 XSS,可能影响父页面
- 数据泄露:通过 postMessage 与 iframe 通信时的信息泄露
- 会话Cookie 泄露:某些情况下子域名 iframe 可以访问父域的 Cookie
二、sandbox 属性
2.1 基本沙箱配置
<iframe sandbox src="page.html"></iframe>
<iframe sandbox="allow-forms allow-scripts" src="page.html"></iframe>
2.2 沙箱选项详解
| 值 |
允许的功能 |
allow-forms |
表单提交 |
allow-modals |
打开模态窗口(alert, prompt 等) |
allow-orientation-lock |
锁定屏幕方向 |
allow-pointer-lock |
使用 Pointer Lock API |
allow-popups |
打开弹窗和窗口 |
allow-popups-to-escape-sandbox |
弹窗可以访问父页面(危险) |
allow-presentation |
使用 Presentation API |
allow-same-origin |
将内容视为同源(会降低安全性) |
allow-scripts |
执行 JavaScript |
allow-top-navigation |
导航父页面 |
allow-top-navigation-by-user-activation |
用户触发后导航父页面 |
allow-storage-access-by-user-activation |
用户触发后访问父页面 Storage |
allow-downloads-with-user-activation |
用户触发后允许下载 |
2.3 安全建议
<iframe
sandbox="allow-scripts allow-same-origin"
src="trusted-content.html"
></iframe>
三、X-Frame-Options
3.1 防止点击劫持
X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN
X-Frame-Options: ALLOW-FROM https://trusted-site.com
app.use((req, res, next) => {
res.setHeader('X-Frame-Options', 'DENY');
next();
});
3.2 CSP frame-ancestors
Content-Security-Policy: frame-ancestors 'none'
Content-Security-Policy: frame-ancestors 'self'
Content-Security-Policy: frame-ancestors trusted-site.com
四、allow 属性
4.1 Permission Policy(原 Feature Policy)
<iframe
src="https://example.com"
allow="camera; microphone; geolocation"
></iframe>
<iframe
src="https://example.com"
allow="geolocation 'self'; camera 'self'"
></iframe>
4.2 常见权限策略
| 特性 |
Permission Policy 值 |
| 地理位置 |
geolocation |
| 摄像头 |
camera |
| 麦克风 |
microphone |
| 支付 |
payment |
| USB |
usb |
| 全屏 |
fullscreen |
| 存储访问 |
storage-access |
五、postMessage 安全
5.1 安全的跨窗口通信
const iframe = document.getElementById('myIframe');
iframe.contentWindow.postMessage('message', 'https://expected-origin.com');
window.addEventListener('message', (event) => {
if (event.origin !== 'https://expected-origin.com') {
return;
}
console.log('Received:', event.data);
});
5.2 验证 source 窗口
const expectedOrigins = ['https://example.com', 'https://app.example.com'];
window.addEventListener('message', (event) => {
if (!expectedOrigins.includes(event.origin)) {
console.warn('Rejected message from:', event.origin);
return;
}
if (event.source !== expectedWindow) {
console.warn('Message from unexpected window');
return;
}
console.log('Received:', event.data);
});
六、实际应用
6.1 安全嵌入第三方内容
<iframe
src="https://third-party-widget.com/widget.html"
sandbox="allow-scripts allow-same-origin"
allow="geolocation 'none'; microphone 'none'; camera 'none'"
loading="lazy"
></iframe>
6.2 检测 iframe 是否被嵌入
if (window.self !== window.top) {
console.log('Running inside iframe');
} else {
console.log('Running in top-level window');
}
6.3 处理 iframe 加载错误
const iframe = document.getElementById('myIframe');
iframe.addEventListener('load', () => {
console.log('Iframe loaded successfully');
});
iframe.addEventListener('error', () => {
console.error('Iframe failed to load');
});
参考资料
延展阅读