Facebook如何禁用浏览器内置开发者工具 技术背景 在网络安全领域,存在一种特定的社会工程攻击,攻击者会诱使用户将恶意 JavaScript 代码粘贴到浏览器控制台中执行。为了保护用户免受此类攻击,Facebook 对部分用户进行测试,尝试禁用浏览器的开发者工具,以减缓这类攻击。不过,从客户端层面阻止黑客攻击通常并非良策,但此措施主要是针对特定的社会工程攻击。
实现步骤 1. 重新定义 console._commandLineAPI
Chrome 会将所有控制台代码包裹在特定代码块中,因此 Facebook 通过重新定义 console._commandLineAPI
来抛出异常,从而阻止代码执行。示例代码如下:
1 2 Object .defineProperty (console , '_commandLineAPI' , { get : function ( ) { throw 'Nooo!' } })
2. 更复杂的实现 为了使控制台自动完成功能失效,并阻止在控制台中输入的语句执行,Facebook 使用了以下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 Object .defineProperty (window , "console" , { value : console , writable : false , configurable : false });var i = 0 ;function showWarningAndThrow ( ) { if (!i) { setTimeout (function ( ) { console .log ("%cWarning message" , "font: 2em sans-serif; color: yellow; background-color: red;" ); }, 1 ); i = 1 ; } throw "Console is disabled" ; }var l, n = { set : function (o ) { l = o; }, get : function ( ) { showWarningAndThrow (); return l; } };Object .defineProperty (console , "_commandLineAPI" , n);Object .defineProperty (console , "__commandLineAPI" , n);
3. 其他实现方式 监听 Function.prototype.call
:通过监听 Function.prototype.call
,当 thisArg
为 evaluate
且第一个参数为 InjectedScriptHost
时,重新定义 evaluate
方法,使其抛出错误。示例代码如下:1 2 3 4 5 6 7 8 9 10 11 12 13 if (window .URL ) { var ish, _call = Function .prototype .call ; Function .prototype .call = function ( ) { if (arguments .length > 0 && this .name === "evaluate" && arguments [0 ].constructor .name === "InjectedScriptHost" ) { ish = arguments [0 ]; ish.evaluate = function (e ) { throw new Error ('Rejected evaluation of: \n\'' + e.split ('\n' ).slice (1 ,-1 ).join ("\n" ) + '\'' ); }; Function .prototype .call = _call; return _call.apply (this , arguments ); } }; }
覆盖 console
对象 :Netflix 通过覆盖 console._commandLineAPI
来抛出安全错误,阻止 JavaScript 在控制台中执行。示例代码如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 (function ( ) { try { var $_console$$ = console ; Object .defineProperty (window , "console" , { get : function ( ) { if ($_console$$._commandLineAPI ) throw "Sorry, for security reasons, the script console is deactivated on netflix.com" ; return $_console$$ }, set : function ($val$$ ) { $_console$$ = $val$$ } }) } catch ($ignore$$) { } })();
4. 禁用开发者工具打开 通过监听 devtools-opened
和 devtools-closed
事件,以及使用 debugger
语句来检测开发者工具是否打开,并执行相应操作。示例代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 (function ( ) { 'use strict' ; Object .getOwnPropertyNames (console ).filter (function (property ) { return typeof console [property] == 'function' ; }).forEach (function (verb ) { console [verb] =function ( ){return 'Sorry, for security reasons...' ;}; }); window .addEventListener ('devtools-opened' , ()=> { window .location .href +="#" ; window .document .head .innerHTML ="" ; window .document .body .innerHTML ="devtools, page is now cleared" ; }); window .addEventListener ('devtools-closed' , ()=> { }); let verifyConsole = ( ) => { var before = new Date ().getTime (); debugger ; var after = new Date ().getTime (); if (after - before > 100 ) { window .dispatchEvent (new Event ('devtools-opened' )); }else { window .dispatchEvent (new Event ('devtools-closed' )); } setTimeout (verifyConsole, 100 ); } verifyConsole (); })();
最佳实践 服务器端验证和安全优先 :客户端禁用开发者工具只能起到一定的辅助作用,服务器端的验证和安全措施才是关键。应该确保服务器端对用户输入进行严格的验证和过滤,防止恶意代码的注入。多文件完整性检查 :可以使用多个 JavaScript 文件来检查页面元素的完整性,如检查脚本文件是否存在、重要元素的属性是否被修改等。一旦发现异常,立即刷新页面。示例代码如下:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 setInterval (function ( ) { var ksExists = document .getElementById ("ksjs" ); if (!ksExists) { location.reload ();}; var psExists = document .getElementById ("psjs" ); if (!psExists) { location.reload ();}; var styleExists = document .getElementById ("style" ); if (!styleExists) { location.reload ();}; }, 1 * 1000 );setInterval (function ( ) { var mainExists = document .getElementById ("mainjs" ); if (!mainExists) { location.reload ();}; var headingExists = document .getElementById ("heading" ); if (!headingExists) { location.reload ();}; var integrityHeading = headingExists.getAttribute ('name' ); if (integrityHeading != 'dontdel' ) { location.reload ();}; var integrity2Heading = headingExists.getAttribute ('value' ); if (integrity2Heading != '2' ) { location.reload ();}; var meta1Exists = document .getElementById ("meta1" ); if (!meta1Exists) { location.reload ();}; var headExists = document .getElementById ("mainhead" ); if (!headExists) { location.reload ();}; }, 1 * 1000 );setInterval (function ( ) { var mainExists = document .getElementById ("mainjs" ); if (!mainExists) { location.reload ();}; var x = document .getElementsByTagName ("meta" )[0 ]; var p = x.getAttribute ("name" ); var s = x.getAttribute ("content" ); if (p != 'description' ) { location.reload (); } if ( s != 'Proper mitigation against script kiddies via Javascript' ) { location.reload (); } var lastMeta = document .getElementsByTagName ("meta" )[1 ]; if (lastMeta) { location.reload (); } }, 1 * 1000 );
常见问题 绕过问题 :客户端禁用开发者工具的方法并非万无一失,攻击者仍然可以通过地址栏、检查元素等方式绕过这些限制。因此,不能仅仅依赖客户端的防护措施,服务器端的安全至关重要。兼容性问题 :不同浏览器对开发者工具的实现方式可能不同,某些方法在某些浏览器中可能无效。例如,Chrome 后来修复了通过 JavaScript 禁用控制台的漏洞,导致之前的方法失效。性能问题 :频繁的完整性检查可能会对页面性能产生影响,特别是在大型页面上。因此,需要根据实际情况调整检查的时间间隔。