1. 程式人生 > 實用技巧 >JS禁止開啟控制檯

JS禁止開啟控制檯

主要為了通過禁止開啟控制檯,防止別人進行程式碼除錯。

1、禁止右鍵檢視原始碼和F12

//禁止F12鍵盤事件
document.addEventListener('keydown', function(event){
return 123 != event.keyCode || (event.returnValue = false)
});
//禁止右鍵、選擇、複製
document.addEventListener(‘'contextmenu'’, function(event){
return event.returnValue = false
})

破解:還可以使用瀏覽器選單中的開發者工具開啟控制檯

2、通過頁面寬度變化監測控制檯

瀏覽寬高變化監測主要是監測瀏覽器可視區域的寬高:window.innerWidth / window.innerHeight(滾動條和內容區)和瀏覽器寬高:window.outerWidth / window.outerHeight(inner的基礎上加上工具條的寬高)之間的差值。

因為我們不知道瀏覽器是否開啟了工具條及工具條的寬高,所以我們設定一個閾值如200,如果outer – inner 大於200,我們就認為開啟了控制檯。

function resize(){
    var threshold = 200;
    var widthThreshold = window.outerWidth - window.innerWidth > threshold;
    var heightThreshold = window.outerHeight - window.innerHeight > threshold;
    if(widthThreshold || heightThreshold){
        console.log('控制檯打開了')
    }
}
window.addEventListener('resize', resize);
resize()

關於檢測視窗大小,有人專門針對此寫了個庫:https://github.com/sindresorh...,感興趣的可以去看一下。

破解:監測瀏覽器寬高變化的缺點是非常明顯的,因為這種監測只能針對控制檯內嵌的情形,但是很多瀏覽器都支援獨立視窗式的控制檯。

3、利用控制檯特性改寫物件toString

對於一些瀏覽器,如果控制檯輸出的是物件,則保留物件的引用,每次開啟控制檯的時候,如果物件型別是function、date等(以前還有regexp,現在已失效),都會重新呼叫一下物件的toString()方法,將返回結果列印到控制檯上。

經過測試:
1)、先宣告物件,再重寫toString,最後列印物件,那麼toString會在開始時多執行一次,所以可以使用一個計數器來判斷哪次有效
2)、先宣告物件,再列印物件,最後重寫toString,那麼如果初始化時控制檯是開啟狀態,會檢測不到這一次的狀態
3)、先宣告物件,再重寫toString,最後列印物件,但是物件不作為第一個引數,此時就可以成功監測每一次控制檯狀態了
4)、console.log、console.info、console.error等均有效
5)、只在chrome核心瀏覽器有效,firefox、ie失效

var devtools = new Date(); //function(){};
devtools.toString = function() {
    console.log('控制檯打開了');
    //或執行一段死迴圈
    window.open("about:blank", "_self"); 
}
console.log('', devtools);

破解:可通過標籤注入js程式碼清空控制檯(新增一個網頁標籤,標籤網址為JavaScript:console.clear();,進入網頁後,點選該標籤頁,就會執行裡面的程式碼),如果是定時器執行上述程式碼,還需要重寫清空

console(JavaScript:console.clear();for(var k in console){if(typeof console[k] == 'function'){console[k] = function(){}}};)。

4、利用控制檯特性進行監聽dom屬性

大部分瀏覽器在列印dom元素的時候,如果控制檯處於關閉狀態,不會獲取元素屬性,但是如果控制檯處於開啟狀態,就會自動獲取dom屬性,從而觸發監聽事件

function observerConsole(){
    //這裡使用dom元素,在開啟控制檯時才會計算id
    var dom = document.createElement("div")
    Object.defineProperty(dom, "id", {
        get: function(){
            console.log('控制檯打開了')
        }
    })
    
    //ie不支援console.table
    //console.info(dom);
    console.log(dom);
}

除了使用console.log,我們還可以使用console.info,console.dir和console.error等等,需要注意的是ie不支援console.table

破解:通過標籤注入js程式碼清空控制檯,如果是定時器執行列印dom的操作,還需要重寫清空console。

上述方法需要注意瀏覽器對於defineProperty的支援,另外在firefox瀏覽器失效,因為firefox瀏覽器對於物件中監聽的屬性不會取值,需要手動點開才會觸發。所以對於firefox需要另闢蹊蹺才行,這裡我選擇使用debugger語句來實現,debugger語句呼叫任何可用的除錯功能,可以阻斷程式碼執行,如果沒有除錯功能可用,則此語句不起作用。所以我們可以在debugger前記錄時間,如果debugger沒有觸發,執行幾條語句的時間幾乎為0,但是如果被觸發,那間隔時間就不是幾十、幾百毫秒了。

function observerConsole(){
    var obj = Object.create(null), t = Date.now();
    Object.defineProperty(obj, "a", {
        get: function() {
            if(Date.now() - t > 100){
                console.log('控制檯打開了')
            }
        }
    })
    setInterval(function(){
        t = Date.now();
        (function(){})["constructor"]("debugger")();//debugger;
        console.log(obj.a);
    }, 200)
}

缺點:如果瀏覽器取消了debugger調式,那麼就毫無意義了。

彙總3、4,可以做如下封裝:

var observerConsole = {
    openCallback: function(){
        console.log('控制檯打開了');
        try {
            window.open("about:blank", "_self")
        } catch(e) {
            var btn = document.createElement("button");
            btn.onclick = function() {
                window.open("about:blank", "_self")
            }
            btn.click()
        }
    },
    observer: function(){
        //這裡使用dom元素,在開啟控制檯時才會計算id
        var dom = document.createElement("div"), that = this;
        Object.defineProperty(dom, "id", {
            get: function(){
                that.openCallback()
            }
        })
        //ie不支援console.table
        //console.info(dom);
        console.log(dom);
    },
    observerF: function(){
        var obj = Object.create(null), t = Date.now(), that = this;
        Object.defineProperty(obj, "a", {
            get: function() {
                if(Date.now() - t > 100){
                    that.openCallback()
                }
            }
        })
        setInterval(function(){
            t = Date.now();
            (function(){})["constructor"]("debugger")();//debugger;
            console.log(obj.a);
        }, 200)
    },
    init: function(){
        var t = window.navigator.userAgent.toLowerCase();
        t.indexOf("firefox") >= 0 ? this.observerF() : this.observer();
    }
}
ConsoleManager.init()

破解:通過標籤注入js程式碼清空控制檯、取消console.log等
反破解:對console.log等進行重寫再包裝,如

let _console = {
      log : console.log,
      info : console.info,
      warn : console.warn,
      error : console.error
};

然後使用_console.log等替換上面的console.log。這裡可以使用閉包,防止別人對_console的再重寫。

程式碼測試僅測試了firefox、ie、chrome瀏覽器及部分chrome核心瀏覽器(如360、qq瀏覽器、UC瀏覽器、搜狗瀏覽器)

資源搜尋網站大全 https://www.renrenfan.com.cn 廣州VI設計公司https://www.houdianzi.com

5、利用debugger的特性,無限遞迴

這個方法不能監測控制檯被開啟,但是能達到不讓別人瀏覽你程式碼的目的。

上面也說了:debugger語句呼叫任何可用的除錯功能,可以阻斷程式碼執行,如果沒有除錯功能可用,則此語句不起作用。

另外:每個瀏覽器都有其最大呼叫棧,如果超出就會丟擲Maximum call stack size exceeded的錯誤並終止程式。

利用上面講的特性組合成下面的程式碼:

function check() {
    function doCheck(a) {
        (function() {}["constructor"]("debugger")()); //debugger
        doCheck(++a);
    }
    try {
        doCheck(0)
    } catch(err) {
        console.log(err)
    }
};

上面程式碼check執行時,如果控制檯未開啟,debugger不會起作用,但是doCheck會不斷迴圈,直至爆棧,丟擲錯誤,中止本次check執行;如果控制檯開啟,則會不斷的進行斷點除錯和迴圈doCheck的呼叫,直至爆棧;如果控制檯開啟,但是取消了debugger調式,雖然此時debugger不會起作用,但遞迴是依然存在的,而且此時網頁效能與未開啟控制檯相比會大幅度下降,嚴重的話,可能會卡死瀏覽器。

未開啟控制檯時程式碼執行時間:
Chrome:30-50ms
Firefox:200-400ms
Ie:10-30ms

開啟控制檯但取消debugger時程式碼執行時間:
Chrome:1000-2000ms
Firefox:頁面直接卡死

從上面的測試結果來看,我們可以設定一個間隔2000ms的定時器來不斷執行check,這樣當控制檯開啟時,不論是否取消debugger調式,都會使頁面卡住。另外我們還可以對程式碼進行混淆,增加閱讀困難度,我們還可以利用閉包完成上面操作,防止別人在控制檯重置check:check=function(){}。

!function(){
    var _0x1cbb = ["tor", "struc", "call", "ger", "con", "bug", "de", "apply"];
    setInterval(check, 2e3);
    function check() {
        function doCheck(_0x1834ff) {
            if (('' + _0x1834ff / _0x1834ff)['length'] !== 0x1 || _0x1834ff % 0x14 === 0x0) {
                (function() {return !![]}[
                    _0x1cbb[0x4] + _0x1cbb[0x1] + _0x1cbb[0x0]
                ](
                    _0x1cbb[0x6] + _0x1cbb[0x5] + _0x1cbb[0x3]
                )[_0x1cbb[0x2]]());
            } else {
                (function() {return ![]}[
                    _0x1cbb[0x4] + _0x1cbb[0x1] + _0x1cbb[0x0]
                ](
                    _0x1cbb[0x6] + _0x1cbb[0x5] + _0x1cbb[0x3]
                )[_0x1cbb[0x7]]());
            }
            doCheck(++_0x1834ff);
        }
        try {
            doCheck(0)
        } catch(err) { }
    };
}();