主要爲了經過禁止打開控制檯,防止別人進行代碼調試。javascript
//禁止F12鍵盤事件 document.addEventListener('keydown', function(event){ return 123 != event.keyCode || (event.returnValue = false) }); //禁止右鍵、選擇、複製 document.addEventListener(‘'contextmenu'’, function(event){ return event.returnValue = false })
破解:還可使用瀏覽器菜單中的開發者工具打開控制檯html
瀏覽寬高變化監測主要是監測瀏覽器可視區域的寬高:window.innerWidth / window.innerHeight(滾動條和內容區)和瀏覽器寬高:window.outerWidth / window.outerHeight(inner的基礎上加上工具條的寬高)之間的差值。java
由於咱們不知道瀏覽器是否開啓了工具條及工具條的寬高,因此咱們設置一個閾值如200,若是outer – inner 大於200,咱們就認爲開啓了控制檯。git
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...,感興趣的能夠去看一下。github
破解:監測瀏覽器寬高變化的缺點是很是明顯的,由於這種監測只能針對控制檯內嵌的情形,可是不少瀏覽器都支持獨立窗口式的控制檯。chrome
對於一些瀏覽器,若是控制檯輸出的是對象,則保留對象的引用,每次打開控制檯的時候,若是對象類型是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(){}}};)。app
大部分瀏覽器在打印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,可是若是被觸發,那間隔時間就不是幾10、幾百毫秒了。
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調式,那麼就毫無心義了。
chrome:
Firefox:
彙總三、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瀏覽器、搜狗瀏覽器)
這個方法不能監測控制檯被打開,可是能達到不讓別人瀏覽你代碼的目的。
上面也說了: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) { } }; }();
優勢:兼容性比較好,不易破解
缺點:會影像瀏覽器性能,形成頁面卡頓
破解:想辦法重置check函數
下面推薦幾個網站,他們都用了上面講的某種方法來禁止打開控制檯,感興趣的話能夠研究下他們是怎麼作到的,該如何破解(破解方法在上面已經給出了)。
如下網站推薦不具備針對性,若有冒犯,十分抱歉,可聯繫我進行刪除