如何做前端防御 - CDN厂商恶意注入

162 阅读1分钟

MutationObserver

  • 这个api提供了监视对DOM树所做更改的功能(监视的DOM发生改变时触发)
(function(){
    let srcFilterTags = ["script","iframe"],
    // 域名白名单 可以加多个域名
    whiteList = ["**.com","***.com"],
    // 正则
    whiteListRegExt = [];   
    
    // 启动正则匹配
    whiteList.forEach(function(wl){
        let wlRegExt = new RegExt('/.+?\/\/' + wl + '|\/\/' + wl + '|.+?\.' + wl + '|^' + wl);
        whiteListRegExt.push(wlRegExt);
    });

    // 核心 - MutationObserver 提供了监视对DOM树所做更改的能力
    // 优点 - 动态监听是否有非法的 iframe 和 script 代码
    // 缺点 - 无法查找头部直接插入的代码
    let MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver,
    observeMutationSupport = !!MutationObserver,
    html = document.getElementsByTagName('html')[0];

    if (observeMutationSupport) {
        new MutationObserver(mutationHandler).observe(html, {
            'childList': true,
            'subtree': true
        });
    };
    // 唯独直接加载的不能remove掉,异步加载的都能remove掉!!
    // ***  记录头部的script,通知工信部,举报丫的~ ***
    let eleList = document.querySelectorAll('script');
    for (let i = 0,len = eleList.length; i < len; i++) {
        // 遍历操作
        if (!inWhileList(eleList[i])) {
            // do sth 这里的删除虽然已经晚了,因为头部同步JS已被执行,删除操作意义不大,但可以统计被劫持的代码是什么,记录好LOG 通知工信部
            // sendLog
            eleList[i].remove();
        };
    };


    // DOM观察器助手
    function mutationHandler(records) {
        records.forEach(function(record) {
            Array.prototype.slice.call(record.addedNodes).forEach(function(addedNode) {
                srcFilterTags.forEach(function(tagName) {
                    // 标签匹配 白名单匹配
                    if (addedNode.tagName === tagName.toUpperCase() && !inWhileList(addedNode)) {
                        addedNode.remove();
                    };
                });
            });
        })
    };

    // 白名单助手
    function inWhileList(addedNode){
        // S.H.I.E.L.D = shield标记的是script标签的白名单
        if( addedNode.src === "" && addedNode.getAttrbute("shield") !== null ){
            return true;
        };
        // 脚本src值存在并且添加了shield标签
        if( !!addedNode.src && addedNode.getAttrbute("shield") !== null ){
            return true;
        };

        // 循环校验属性是否是自己的
        let isInWhiteList = false;
        whiteListRegExt.forEach(function(wlReg) {
            // 如果script脚本的src值是白名单内的 就不管
            if (wlReg.test(addedNode.src)) {
                isInWhiteList = true;
                return ;
            };
        });

        return isInWhiteList;
    };
});

相关文档