Facebook如何禁用浏览器的集成开发者工具?
显然,由于最近发生的诈骗事件,开发者工具被人们利用来发布垃圾邮件,甚至用于“黑客”账户。 Facebook已经阻止了开发者工具,我甚至不能使用控制台。
他们是怎么做到的? 一个堆栈溢出帖子声称这是不可能的 ,但Facebook已经证明他们错了。
只要进入Facebook并打开开发者工具,在控制台中输入一个字符,这个警告弹出。 不管你输入什么,它都不会被执行。
这怎么可能?
他们甚至在控制台中阻止自动完成:
我是Facebook的安全工程师,这是我的错。 我们正在对一些用户进行测试,看看是否可以减缓用户被欺骗(恶意)JavaScript代码粘贴到浏览器控制台的一些攻击。
只是要清楚:试图阻止黑客的客户端是一个普遍的坏主意 , 这是为了防止特定的社会工程攻击 。
如果你最终进入了测试组,并为此感到恼火,对不起。 我试图尽可能简单地使旧的退出页面(现在的帮助页面 )尽可能简单,至少可以阻止一些受害者。
实际的代码非常类似于@ joeldixon66的链接 ; 没有很好的理由,我们的情况稍微复杂一些。
Chrome包含所有控制台代码
with ((console && console._commandLineAPI) || {}) { <code goes here> }
…所以网站重新定义了console._commandLineAPI
抛出:
Object.defineProperty(console, '_commandLineAPI', { get : function() { throw 'Nooo!' } })
这是不够的(尝试!) ,但这是主要的伎俩。
结语:Chrome团队决定从用户端JS击败控制台是一个错误,并解决了这个问题 ,使这种技术无效。 之后,增加了额外的保护,以保护用户免受自我xss 。
我使用Chrome开发人员工具查找了Facebook的控制台拦截器脚本。 这里是脚本的可读性稍作改动。 我删除了我无法理解的部分:
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);
这样,控制台自动完成失败,而在控制台中输入的语句将无法执行(将记录异常)。
参考文献:
- Object.defineProperty
- Object.getOwnPropertyDescriptor
- Chrome的console.log函数(有关格式化输出的提示)
我无法在任何页面上触发它。 一个更健壮的版本将做到这一点:
window.console.log = function(){ console.error('The developer console is temp...'); window.console.log = function() { return false; } } console.log('test');
在JavaScript控制台中设置输出风格: 颜色
编辑 Thinking @ joeldixon66有正确的想法: 从控制台禁用JavaScript执行«::: KSpace :::
除了重新定义console._commandLineAPI
,还有其他一些方法可以在WebKit浏览器中分解为InjectedScriptHost,以防止或改变对输入到开发者控制台的表达式的评估。
编辑:
Chrome已经在过去的版本中解决了这个问题。 – 那个时候一定是在2015年2月之前,因为那个时候我创造了这个主意
所以这是另一种可能性。 这一次,我们把一个上面的级别直接插入InjectedScript
而不是InjectedScriptHost
,而不是之前的版本。
这是一种很好的,因为你可以直接猴子补丁InjectedScript._evaluateAndWrap
而不是依靠InjectedScriptHost.evaluate
因为这给你更细粒度的控制应该发生。
另一个非常有趣的事情是,我们可以在评估表达式时拦截内部结果,并将其返回给用户而不是正常行为。
这里是代码,那就是当用户在控制台中评估某些东西时返回内部结果。
var is; Object.defineProperty(Object.prototype,"_lastResult",{ get:function(){ return this._lR; }, set:function(v){ if (typeof this._commandLineAPIImpl=="object") is=this; this._lR=v; } }); setTimeout(function(){ var ev=is._evaluateAndWrap; is._evaluateAndWrap=function(){ var res=ev.apply(is,arguments); console.log(); if (arguments[2]==="completion") { //This is the path you end up when a user types in the console and autocompletion get's evaluated //Chrome expects a wrapped result to be returned from evaluateAndWrap. //You can use `ev` to generate an object yourself. //In case of the autocompletion chrome exptects an wrapped object with the properties that can be autocompleted. eg; //{iGetAutoCompleted: true} //You would then go and return that object wrapped, like //return ev.call (is, '', '({test:true})', 'completion', true, false, true); //Would make `test` pop up for every autocompletion. //Note that syntax as well as every Object.prototype property get's added to that list later, //so you won't be able to exclude things like `while` from the autocompletion list, //unless you wou'd find a way to rewrite the getCompletions function. // return res; //Return the autocompletion result. If you want to break that, return nothing or an empty object } else { //This is the path where you end up when a user actually presses enter to evaluate an expression. //In order to return anything as normal evaluation output, you have to return a wrapped object. //In this case, we want to return the generated remote object. //Since this is already a wrapped object it would be converted if we directly return it. Hence, //`return result` would actually replicate the very normal behaviour as the result is converted. //to output what's actually in the remote object, we have to stringify it and `evaluateAndWrap` that object again.` //This is quite interesting; return ev.call (is, null, '(' + JSON.stringify (res) + ')', "console", true, false, true) } }; },0);
这有点冗长,但是我想我已经写了一些评论
所以通常情况下,如果一个用户评估[1,2,3,4]
你会期望得到如下的结果:
在monkedypatching InjectedScript._evaluateAndWrap
评估相同的表达式后,给出以下输出:
当你看到指示输出的小箭头,仍然存在,但这次我们得到一个对象。 在表达式的结果中,数组[1,2,3,4]
表示为一个对象,其所有属性都被描述。
我建议尝试评估这个和那个表达式,包括那些产生错误的表达式。 这很有趣。
另外,看看is
– InjectedScriptHost
–对象。 它提供了一些方法来玩,并对督察的内部有一点洞察力。
当然,你可以拦截所有的信息,并将原始结果返回给用户。
只需用一个console.log (res)
替换else路径中的return语句即可。 那么你最终会得到以下结果。
编辑结束
这是谷歌已经修复的以前的版本。 因此不是一个可能的方式了。
其中之一是挂钩到 Function.prototype.call
Chrome通过将thisArg
eval函数call
为thisArg
来评估输入的表达式
var result = evalFunction.call(object, expression);
鉴于此,您可以侦听正在evaluate
的call
这个thisArg
,并获得对第一个参数( InjectedScriptHost
)的引用
if (window.URL) { var ish, _call = Function.prototype.call; Function.prototype.call = function () { //Could be wrapped in a setter for _commandLineAPI, to redefine only when the user started typing. if (arguments.length > 0 && this.name === "evaluate" && arguments [0].constructor.name === "InjectedScriptHost") { //If thisArg is the evaluate function and the arg0 is the ISH ish = arguments[0]; ish.evaluate = function (e) { //Redefine the evaluation behaviour throw new Error ('Rejected evaluation of: \n\'' + e.split ('\n').slice(1,-1).join ("\n") + '\''); }; Function.prototype.call = _call; //Reset the Function.prototype.call return _call.apply(this, arguments); } }; }
你可以抛出一个错误,评价被拒绝。
下面是一个例子 ,其中输入的表达式在传递给evaluate
函数之前被传递给一个CoffeeScript编译器。
Netflix也实现了这个功能
(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$$) { } })();
他们只是重写console._commandLineAPI
抛出安全错误。
这实际上是可能的,因为Facebook能够做到这一点。 那么,不是实际的Web开发人员工具,而是在控制台中执行Javascript。
看到这个: Facebook如何禁用浏览器的集成开发工具?
这真的不会做太多,因为有其他的方法来绕过这种类型的客户端安全。
当你说它是客户端时,它发生在服务器的控制之外,所以你可以做的事情不多。 如果你问为什么Facebook仍然这样做,这不是真正的安全,而是保护不知道JavaScript的普通用户从运行代码(他们不知道如何阅读)到控制台。 这是常见的承诺自动liker服务或其他Facebook功能的机器人,你做他们要求你做的地方,在大多数情况下,他们给你一个JavaScript的剪辑在控制台中运行。
如果你的用户没有Facebook那么多,那么我认为没有必要去做Facebook的工作。
即使您在控制台中禁用Javascript,仍然可以通过地址栏运行JavaScript。
如果浏览器在地址栏中禁用javascript(当您将代码粘贴到Google Chrome中的地址栏时,它会删除“javascript:”这个短语),通过检查元素粘贴javascript到其中一个链接仍然是可能的。
检查锚点:
将代码粘贴到href:
底线是服务器端验证和安全性应该是第一,然后做客户端后。
Chrome浏览器改变了很多,因为Facebook可以禁用控制台。
根据2017年3月,这不再工作。
最好你可以做的是禁用一些控制台功能,例如:
if(!window.console) window.console = {}; var methods = ["log", "debug", "warn", "info", "dir", "dirxml", "trace", "profile"]; for(var i=0;i<methods.length;i++){ console[methods[i]] = function(){}; }
我简单的方法,但它可以帮助进一步变化这个问题。 列出所有方法并将其改为无用。
Object.getOwnPropertyNames(console).filter(function(property) { return typeof console[property] == 'function'; }).forEach(function (verb) { console[verb] =function(){return 'Sorry, for security reasons...';}; });