本文目录导读:

- 内容完整性校验(Subresource Integrity, SRI)
- DOM 元素内容/属性对比校验
- 元素存在性与异常注入检测
- 完整性监控(MutationObserver)
- 后端签名与前端验证
- 防篡改容器与“看门狗”脚本
- 总结与建议
检测页面篡改(即检测网页是否被恶意修改或注入恶意代码)通常涉及前端和后端的结合,由于脚本(通常指 JavaScript)在用户浏览器中运行,它本身也可能被篡改,因此需要配合服务端校验或使用浏览器安全特性。
以下是几种常见的脚本检测页面篡改方法,从简单到复杂:
内容完整性校验(Subresource Integrity, SRI)
这是最推荐的防御方式之一,由浏览器原生支持,它用于检测引用的外部脚本或样式文件是否被篡改。
- 原理:在
<script>或<link>标签中添加integrity属性,浏览器会计算加载文件的哈希值,并与提供的哈希值比对,若不匹配,则阻止执行。 - 适用场景:防止 CDN 上的文件被篡改。
示例:
<script src="https://example.com/analytics.js"
integrity="sha384-oqVuAfXRKap7fdgcCY5uykM6+R9GqQGTkchBcPkYdA=="
crossorigin="anonymous"></script>
analytics.js 被修改,浏览器会拒绝加载并在控制台报错。
DOM 元素内容/属性对比校验
通过 JavaScript 定期检查页面关键节点的内容是否与预期一致。
- 步骤:
- 选择一个或多个关键且稳定的 DOM 节点(如
document.title,<head>中的某个 meta 标签,或<body>中的固定文本)。 - 在页面加载时,将该节点的内容(
innerHTML、textContent、innerText或特定属性)加密(如使用 SHA-256)并记录或发送到服务器作为基准。 - 定期(如每隔 5 秒)重新获取该节点内容并重新哈希,与基准值比对。
- 选择一个或多个关键且稳定的 DOM 节点(如
- 缺点:
- 容易被 JS 禁用或 Hook。
- 动态页面内容频繁变化会导致大量误报。
- 检测代码本身可能被篡改。
元素存在性与异常注入检测
检测页面中是否出现了预期之外的元素。
- 检测点:
- 检查
document.body.appendChild是否被重写或监控(检测动态注入)。 - 检测是否包含不熟悉的
<script>标签(通常恶意代码会注入新的<script>)。 - 检查
innerHTML操作是否出现异常关键字。
- 检查
- 简单检测示例:
(function detectInjection() { // 检查页面中的 <script> 标签数量(如果原本只有 2 个,现在出现 5 个) const scripts = document.getElementsByTagName('script'); if (scripts.length > EXPECTED_SCRIPT_COUNT) { console.warn('检测到异常脚本注入!'); // 可以记录异常,但不要在这里弹窗,因为弹窗也可能被劫持 } })(); - 缺点:容易误报,且可以被针对。
完整性监控(MutationObserver)
利用浏览器的 MutationObserver API 监控 DOM 变化,检测是否有非预期的修改。
- 原理:监听
childList、attributes、subtree等变化。 - 操作:
- 当 DOM 发生变化时,检查变化的节点是否在白名单中(某个动态广告位)。
- 如果不是,且来源不是已知的扩展或受信任脚本,则视为可疑篡改。
示例(监控 body 新增节点):
const targetNode = document.body;
const config = { childList: true, subtree: true };
const observer = new MutationObserver((mutationsList) => {
for (const mutation of mutationsList) {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach(node => {
if (node.nodeType === 1 && node.tagName === 'SCRIPT') {
// 检测到非授权的脚本节点被添加到 body
console.warn('可能被注入了脚本:', node.src || node.textContent);
}
});
}
}
});
observer.observe(targetNode, config);
后端签名与前端验证
这是一种较强的防御方式,服务端对页面关键部分(如 HTML 主体或关键数据)生成一个带密钥的 HMAC 签名,并将其嵌入页面(通常放在一个不可见的 <script> 或 <meta> 中),前端 JS 在页面加载后用预置的公钥或相同密钥验证签名。
- 优点:攻击者若不知道密钥,无法伪造签名。
- 缺点:
- 前端密钥会暴露,容易被逆向,若使用对称密钥,密钥泄露则失效。
- 需要服务端支持。
防篡改容器与“看门狗”脚本
- 原理:
- 将核心业务逻辑放在一个闭包或沙箱环境中。
- 建立一个独立的“看门狗”脚本,该脚本定期向服务端发送心跳,并请求服务端返回当前页面关键哈希值,然后在本地比对。
- “看门狗”脚本是动态生成的,每次请求的算法或密钥可能不同。
总结与建议
| 方法 | 强度 | 实现难度 | 误报率 | 推荐指数 |
|---|---|---|---|---|
| SRI | 高(外部资源) | 低 | 低 | ⭐⭐⭐⭐⭐ |
| 对比 | 中 | 中 | 高 | ⭐⭐ |
| 元素存在性检测 | 低 | 低 | 中 | ⭐ |
| MutationObserver | 中 | 中 | 中 | ⭐⭐⭐ |
| 后端签名验证 | 高 | 高 | 低 | ⭐⭐⭐⭐ |
| 看门狗脚本 | 高 | 很高 | 低 | ⭐⭐⭐⭐ |
实际落地建议:
- 首选 SRI:为所有外部 CDN 资源启用,这是防篡改的第一道防线,且无需改动业务逻辑。
- 结合 CSP:使用 Content Security Policy (CSP) 限制允许执行的脚本源(
script-src)和允许内联代码的哈希/Nonce,这能有效阻止绝大多数 XSS 和脚本注入导致的页面篡改。 - 关键页面增加服务端校验:对于登录、支付等高风险页面,可实施后端签名验证。
- 内联脚本使用 Nonce:对页面内的
<script>标签使用 Nonce 属性,配合 CSP 防止非授权脚本执行。 - 定期扫描与监控:在浏览器端(通过自己的脚本)或服务器端(通过爬虫)定期抓取并比对页面快照,这是发现长期篡改的有效手段。
最后需要明白:没有任何前端脚本能100%防止页面篡改,因为攻击者如果控制了整个页面,同样可以篡改你的检测脚本,前端检测更多是作为一种监控和告警手段,而不是彻底防御,真正的安全需要服务端、网络传输、前端策略(CSP, SRI)等多层协同。