Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

防运营商劫持的基本操作 #17

Open
mytac opened this issue Nov 22, 2018 · 0 comments
Open

防运营商劫持的基本操作 #17

mytac opened this issue Nov 22, 2018 · 0 comments
Assignees
Labels

Comments

@mytac
Copy link
Owner

mytac commented Nov 22, 2018

不知道大家有没有这样的体验,当你专心浏览网站内容的时候,会从右下角弹出小广告,当你点击关闭的时候,又会跳转到另外的链接,你以为是网站搞的鬼,但你尝试去联系管理员时,人家表示不背这个锅。其实这背后是你的网络运营商在借刀杀人~

市面上常见的运营商劫持主要是http劫持,https劫持和dns劫持。我们经常看到的右下角弹小广告的一般是http劫持,如果将http转为https可以减少被运营商劫持的机率,但也可能会被伪造证书,仍然会被劫持。所以我们需要在自己的程序中设置一个监听器来监控dom中劫持插入的脚本。

思路

需要在运营商插入非法代码之前监听dom变动,以下的例子只检查<script>标签。

step1.挂载dom监听器

主要使用html5的一个特性MutationObserver来观察dom变动,注意兼容性,如果你不打算兼容ie浏览器,MutationObserver是最好的选择。

MutationObserver兼容性图示

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)
    }
}

举报方法

按下图方法投诉到工信部,一步到位!

投诉到工信部

参考链接

  1. 干货!防运营商劫持
  2. 【HTTP劫持和DNS劫持】实际JS对抗
  3. MutationObserver
@mytac mytac added the OTHER label Nov 22, 2018
@mytac mytac self-assigned this Nov 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant