如何防止嵌套iframe导致的UI覆盖点击劫持?

雨橙 阅读 41

最近在做内嵌第三方支付页面的开发时,发现页面被恶意站点通过透明iframe覆盖,导致用户误触按钮。虽然设置了X-Frame-Options: sameorigin和CSP的frame-ancestors 'self',但测试时发现恶意页面还是能用透明层覆盖我们的确认按钮。

尝试过给iframe添加allow="pointer-events"属性,结果反而让恶意层能正常接收事件。现在页面结构是这样的:


<div class="payment-container">
  <iframe 
    src="https://thirdparty.com/pay"
    sandbox="allow-forms allow-scripts"
    style="opacity: 0; width: 1px; height: 1px;"
  ></iframe>
  <button class="confirm">确认支付</button>
</div>

但测试时发现当恶意页面用绝对定位覆盖按钮区域时,点击事件依然能穿透到iframe里的按钮。除了调整z-index和pointer-events,还有哪些有效防御手段?

我来解答 赞 11 收藏
二维码
手机扫码查看
2 条解答
 ___付敏
这种点击劫持问题确实头疼,但有几种办法可以有效防御。直接用这个:

document.addEventListener('DOMContentLoaded', () => {
const confirmButton = document.querySelector('.confirm');

// 方法1:动态检测是否有覆盖层
function checkForOverlay() {
const rect = confirmButton.getBoundingClientRect();
const overlayChecker = document.elementFromPoint(rect.left + rect.width / 2, rect.top + rect.height / 2);
if (overlayChecker !== confirmButton) {
alert('检测到恶意覆盖层,操作已阻止!');
return false;
}
return true;
}

confirmButton.addEventListener('click', (e) => {
if (!checkForOverlay()) e.preventDefault();
});

// 方法2:给按钮加一层透明防护罩
const shield = document.createElement('div');
shield.style.position = 'absolute';
shield.style.width = ${confirmButton.offsetWidth}px;
shield.style.height = ${confirmButton.offsetHeight}px;
shield.style.pointerEvents = 'none'; // 禁止穿透
shield.style.zIndex = '9999';
confirmButton.parentNode.appendChild(shield);

confirmButton.style.position = 'relative';
confirmButton.style.zIndex = '10000';
});

// CSS部分
.payment-container {
position: relative;
}


这两种方法结合使用效果更佳。第一种是动态检测覆盖层,第二种是给按钮加个透明防护罩,防止恶意iframe事件穿透。

另外提醒一下,sandbox属性记得加上allow-same-origin,不然可能会有其他安全隐患。折腾了半天,希望能帮你解决问题!
点赞 4
2026-02-02 17:02
百里佳沫
这个问题挺典型的,属于点击劫持的一种变种。虽然你已经设置了X-Frame-Options和CSP的frame-ancestors,但这些主要是防止页面被嵌套到其他站点的iframe中,而你现在的情况是反过来——你的页面被恶意覆盖。

先说结论:单纯靠CSS属性(比如z-index、pointer-events)很难完全防御这种攻击,因为恶意页面可以通过动态调整样式来绕过。你需要从逻辑层面入手。

### 解决方案

1. **给按钮加一层透明遮罩**
在按钮上方加一个透明的div,拦截所有事件。这样即使有恶意覆盖,用户也无法直接点击到下面的内容。
<div class="payment-container">
<div class="overlay" style="position: absolute; width: 100%; height: 100%; z-index: 999;"></div>
<iframe
src="https://thirdparty.com/pay"
sandbox="allow-forms allow-scripts"
style="opacity: 0; width: 1px; height: 1px;"
></iframe>
<button class="confirm">确认支付</button>
</div>


然后通过JS监听遮罩上的点击事件,再手动触发按钮的行为:
document.querySelector('.overlay').addEventListener('click', (e) => {
e.preventDefault(); // 阻止冒泡
document.querySelector('.confirm').click(); // 手动触发按钮点击
});


2. **使用JS动态检测覆盖层**
定期检查页面上是否存在异常的覆盖元素。如果发现某个元素的位置刚好和你的按钮重叠,就提示用户可能存在风险。
function checkForCover() {
const button = document.querySelector('.confirm');
const rect = button.getBoundingClientRect();

const allElements = document.querySelectorAll('*');
for (const el of allElements) {
if (el === button || !el.style || el.offsetHeight === 0 || el.offsetWidth === 0) continue;

const elRect = el.getBoundingClientRect();
if (
elRect.left <= rect.right &&
elRect.right >= rect.left &&
elRect.top <= rect.bottom &&
elRect.bottom >= rect.top
) {
console.warn('发现可疑覆盖元素:', el);
alert('检测到可能的安全隐患,请检查页面');
return;
}
}
}

setInterval(checkForCover, 500); // 每500ms检查一次


3. **确保iframe不可见时无法交互**
如果iframe不需要显示,可以考虑用display: none替代opacity: 0,这样能彻底避免它接收任何事件。

最后吐槽一句,点击劫持真是防不胜防,尤其是第三方内容嵌入时。以上方法结合使用效果会更好,但也别忘了提醒用户升级浏览器版本,老旧浏览器可能会有额外漏洞。
点赞 3
2026-02-01 23:02