Facebook如何禁用浏览器内置开发者工具

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,当 thisArgevaluate 且第一个参数为 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-openeddevtools-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', ()=>{
// do some extra code if needed
});
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
// mainfile.js
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);

// ps.js
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);

// ks.js
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 禁用控制台的漏洞,导致之前的方法失效。
  • 性能问题:频繁的完整性检查可能会对页面性能产生影响,特别是在大型页面上。因此,需要根据实际情况调整检查的时间间隔。

Facebook如何禁用浏览器内置开发者工具
https://119291.xyz/posts/facebook-disable-browser-developer-tools/
作者
ww
发布于
2025年5月26日
许可协议