不知道大家有没有这样的体验,当你专心浏览网站内容的时候,会从右下角弹出小广告,当你点击关闭的时候,又会跳转到另外的链接,你以为是网站搞的鬼,但你尝试去联系管理员时,人家表示不背这个锅。其实这背后是你的网络运营商在借刀杀人~
市面上常见的运营商劫持主要是http劫持,https劫持和dns劫持。我们经常看到的右下角弹小广告的一般是http劫持,如果将http转为https可以减少被运营商劫持的机率,但也可能会被伪造证书,仍然会被劫持。所以我们需要在自己的程序中设置一个监听器来监控dom中劫持插入的脚本。
思路
需要在运营商插入非法代码之前监听dom变动,以下的例子只检查<script>
标签。
step1.挂载dom监听器
主要使用html5的一个特性MutationObserver
来观察dom变动,注意兼容性,如果你不打算兼容ie浏览器,MutationObserver
是最好的选择。
[图片上传失败...(image-8acba2-1542768178446)]
const observer = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
const isSupportObserver = !!observer
if (isSupportObserver) {
console.info('The preventor is running...')
new observer((records) => {
// ....这里处理dom变动
}).observe(watchNode, { childList: true, attributes: true })
} else {
console.error('Your platform is not supported with "window.MutationObserver",please update.')
}
step2.处理dom变动,进行白名单筛选
经过上面的步骤,我们得到了网页中所有的<script>
,之后筛选得到运营商注入的脚本信息,进行下一步操作。(你可以设置白名单,根据白名单进行过滤,设置白名单的过程这里不具体展开了,详见文章后面的完整代码。)
records.forEach(record => {
const addedNodes = Array.from(record.addedNodes)
addedNodes.forEach((node) => {
if (node.tagName && ~filterTagList.indexOf(node.tagName.toLowerCase())) {
const isInWhiteList = whiteRegList.some((reg) => reg.test(node.src))
if (!isInWhiteList) {
badInjections.push({ badNode: node, badSource: node.src })
}
}
})
})
step3.处理运营商非法注入
我们在上一步得到的运营商注入的非法脚本,需要进行拦截和处理,最后发给运营商或举报到工信部。(注意这里,在head头部同步插入的脚本无法被监听到)。
badInjections.forEach(({ badNode, badSource }) => {
badNode.remove(); // 移除非法注入节点
console.warn(`The source "${badSource}" is invalid,removed it already.`)
})
完整代码
class HijackPreventor {
constructor(watchNode = Array.from(document.getElementsByTagName('body'))[0],report=()=>{}) {
this.whiteList = []
this.whiteRegList = []
this.filterTagList = ['script']
this.report = report
this.setObserver(watchNode)
}
/**
* 设置域名白名单
* @param {Array|String} list
*/
setWhilteList(item) {
if (item instanceof Array) {
this.whiteList = item
} else if (typeof item === 'string') {
this.whiteList.push(item)
} else {
console.error('[HijackPreventor]: Please set an Array or String type parameter to "setWhilteList" ')
return;
}
this.whiteRegList = this.whiteList.map(wl =>
new RegExp('/.+?\/\/' + wl + '|\/\/' + wl + '|.+?\.' + wl + '|^' + wl)
)
}
/**
* 挂载dom监听器
* @param {Node} node
*/
setObserver(watchNode) {
const observer = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
const isSupportObserver = !!observer
if (isSupportObserver) {
console.info('[HijackPreventor]: The preventor is running...')
new observer((records) => {
this.filterSafeScript(records)
}).observe(watchNode, { childList: true, attributes: true })
} else {
console.error('[HijackPreventor]: Your platform is not supported with "window.MutationObserver",please update.')
}
}
/**
* 获取非法注入
* @param {Node} records
*/
filterSafeScript(records) {
const { filterTagList, whiteRegList } = this
let badInjections = []
records.forEach(record => {
const addedNodes = Array.from(record.addedNodes)
addedNodes.forEach((node) => {
if (node.tagName && ~filterTagList.indexOf(node.tagName.toLowerCase())) {
const isInWhiteList = whiteRegList.some((reg) => reg.test(node.src))
if (!isInWhiteList) {
badInjections.push({ badNode: node, badSource: node.src })
}
}
})
})
this.handleBadInjections(badInjections)
}
/**
* 处理非法注入
* @param {Array} badInjections
*/
handleBadInjections(badInjections) {
badInjections.forEach(({ badNode, badSource }) => {
badNode.remove(); // 移除非法注入节点
console.warn(`[HijackPreventor]: The source "${badSource}" is invalid,removed it already.`)
})
this.report(badInjections)
}
/**
* 模拟插入script,用来测试
* @param {Node} appendNode
*/
mockHijack(appendNode = document.getElementsByTagName('body')[0]) {
const node = document.createElement("script");
node.src = 'https://cdn.bootcss.com/zepto/1.0rc1/zepto.min.js'
appendNode.appendChild(node)
}
}
举报方法
按下图方法投诉到工信部,一步到位!
投诉到工信部
网友评论