(一)前言
准确是防运营商劫持就是防script、iframe注入型劫持,属于HTTP劫持中的一种。主要是劫持js文件,然后在网页中通过js脚本往网页中注入图片和链接或者框架广告,也叫流量劫持。
至于劫持类型,这里我建议,看这篇文章
(二)实现
对于js注入,我们可以使用一个新API
这里是目前的浏览器兼容性
所以,我们可以看到目前所有浏览器的兼容性是92.79%,手机端的兼容性更高。那么我们是如何实现的
这里我们要看MutationObserver,它会监听指定的DOM发生变化时被调用。那么如果将script插入时候判断下,那么就能过滤掉注入的js文件,iframe,防止js修改我们页面。当然我们还需要增加白名单和安全标签(shendun-eddy),防止CDN被删除。
(二)具体代码
<script shendun-eddy>
/**
* @note 防劫持代码
* @key MutationObserver 提供了监视对DOM树所做更改的能力
*/
(function() {
try {
var srcFilterTags = ['script', 'iframe'];
// 域名白名单 可以加多个
var whiteList = [
window.location.hostname,
'unpkg.com',
'polyfill.io',
'cnzz.com',
];
// 资源链接匹配规则,元素类型为正则表达式
var whiteListReg = [];
// 正则匹配
whiteList.forEach(function(wl) {
// 匹配http开头的域名部分,元素实例会自行修正src中的反斜杠
var wlReg = new RegExp('^https?:(//)?[^/]*?' + wl, 'i');
whiteListReg.push(wlReg);
});
// 白名单助手
var inWhileList = function(addedNode) {
// 例外规则
if (
addedNode.src === '' &&
// shendun-eddy 是script的白名单标签
(addedNode.getAttribute('shendun-eddy') !== null ||
// iframe空src,html2canvas需要iframe做兼容性检测
addedNode.tagName.toLowerCase() === 'iframe')
) {
return true;
}
var isInWhiteList = false;
whiteListReg.forEach(function(wlReg) {
if (wlReg.test(addedNode.src)) {
isInWhiteList = true;
return false;
}
});
return isInWhiteList;
};
// dom观察器
var mutationHandler = function(records) {
records.forEach(function(record) {
var addedNodes = [];
// 如果src被动态修改,也要进行监管
if (
record.type.toLowerCase() === 'attributes' &&
record.attributeName.toLowerCase() === 'src'
) {
addedNodes.push(record.target);
} else {
addedNodes = Array.prototype.slice.call(record.addedNodes);
}
addedNodes.forEach(function(addedNode) {
srcFilterTags.forEach(function(tagName) {
// 标签匹配 白名单匹配
if (addedNode.tagName === tagName.toUpperCase() && !inWhileList(addedNode)) {
addedNode.remove();
}
});
});
});
};
var MutationObserver =
window.MutationObserver ||
window.WebKitMutationObserver ||
window.MozMutationObserver,
observeMutationSupport = !!MutationObserver;
if (observeMutationSupport) {
new MutationObserver(mutationHandler).observe(document.documentElement, {
childList: true,
subtree: true,
// 监视src属性修改
/**
* android 4.4.2 需要声明 attributes
* 不声明则attributeFilter则报异常 Uncaught SyntaxError: An invalid or illegal string was specified.
*/
attributes: true,
attributeFilter: ['src']
});
}
var eleList = document.querySelectorAll('script');
var len = eleList.length;
for (var i = 0; i < len; i++) {
if (!inWhileList(eleList[i])) {
eleList[i].remove();
}
}
} catch (error) {
console.error(error);
}
if (window.self !== window.top) {
alert(
'脚本检测到存在劫持的风险,如页面不属于请不予理会!',
);
}
})();
</script>
(四)总结
当我们网站加入防运营商劫持代码,能防大部分注入型劫持,当然这是第一步,接下来我们要讲解下CSP( 内容安全策略)防运营商劫持