Facebook如何禁用浏览器的集成开发人员工具?

本文翻译自:How does Facebook disable the browser's integrated Developer Tools?

So apparently because of the recent scams, the developer tools is exploited by people to post spam and even used to "hack" accounts. 因此,显然是由于最近的骗局,开发人员工具被人们用来发布垃圾邮件,甚至被用来“破解”帐户。 Facebook has blocked the developer tools, and I can't even use the console. Facebook阻止了开发人员工具,我什至不能使用该控制台。

在此处输入图片说明

How did they do that?? 他们是怎么做到的?? One Stack Overflow post claimed that it is not possible , but Facebook has proven them wrong. 一篇Stack Overflow帖子声称这是不可能的 ,但是Facebook已经证明它们是错误的。

Just go to Facebook and open up the developer tools, type one character into the console, and this warning pops up. 只需转到Facebook并打开开发人员工具,在控制台中键入一个字符,就会弹出此警告。 No matter what you put in, it will not get executed. 不管您输入什么内容,都不会执行它。

How is this possible? 这怎么可能?

They even blocked auto-complete in the console: 他们甚至阻止了控制台中的自动完成:

在此处输入图片说明


#1楼

参考:https://stackoom.com/question/1T1FO/Facebook如何禁用浏览器的集成开发人员工具


#2楼

I couldn't get it to trigger that on any page. 我无法在任何页面上触发它。 A more robust version of this would do it: 一个更强大的版本可以做到这一点:

window.console.log = function(){
    console.error('The developer console is temp...');
    window.console.log = function() {
        return false;
    }
}

console.log('test');

To style the output: Colors in JavaScript console 设置输出样式: JavaScript控制台中的颜色

Edit Thinking @joeldixon66 has the right idea: Disable JavaScript execution from console « ::: KSpace ::: 编辑思维@ joeldixon66有一个正确的主意: 从控制台«::: KSpace :::禁用JavaScript执行


#3楼

I'm a security engineer at Facebook and this is my fault. 我是Facebook的安全工程师,这是我的错。 We're testing this for some users to see if it can slow down some attacks where users are tricked into pasting (malicious) JavaScript code into the browser console. 我们正在为某些用户进行测试,以查看它是否可以减慢某些诱使用户将(恶意)JavaScript代码粘贴到浏览器控制台的攻击。

Just to be clear: trying to block hackers client-side is a bad idea in general; 只是要清楚一点:尝试阻止客户端的黑客通常是一个坏主意 this is to protect against a specific social engineering attack . 这是为了防止特定的社会工程攻击

If you ended up in the test group and are annoyed by this, sorry. 如果您最终参加了测试小组并为此感到烦恼,请对不起。 I tried to make the old opt-out page (now help page ) as simple as possible while still being scary enough to stop at least some of the victims. 我试图使旧的退出页面(现在是帮助页面 )尽可能简单,同时仍然令人恐惧,无法阻止至少一些受害者。

The actual code is pretty similar to @joeldixon66's link ; 实际的代码与@ joeldixon66的链接非常相似; ours is a little more complicated for no good reason. 没有充分的理由,我们的情况会稍微复杂一些。

Chrome wraps all console code in Chrome将所有控制台代码包装在其中

with ((console && console._commandLineAPI) || {}) {
  <code goes here>
}

... so the site redefines console._commandLineAPI to throw: ...因此该网站重新定义了console._commandLineAPI以抛出:

Object.defineProperty(console, '_commandLineAPI',
   { get : function() { throw 'Nooo!' } })

This is not quite enough (try it!) , but that's the main trick. 这还不够(尝试一下!) ,但这是主要技巧。


Epilogue: The Chrome team decided that defeating the console from user-side JS was a bug and fixed the issue , rendering this technique invalid. 结语:Chrome小组认为从用户端JS击败控制台是一个错误,并解决了该问题 ,使该技术无效。 Afterwards, additional protection was added to protect users from self-xss . 之后,添加了附加保护以保护用户免受self-xss的攻击


#4楼

I located the Facebook's console buster script using Chrome developer tools. 我使用Chrome开发人员工具找到了Facebook的控制台破坏脚本。 Here is the script with minor changes for readability. 这是为了可读性而进行了微小更改的脚本。 I have removed the bits that I could not understand: 我删除了我无法理解的部分:

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);

With this, the console auto-complete fails silently while statements typed in console will fail to execute (the exception will be logged). 这样,控制台自动完成功能会静默失败,而在控制台中键入的语句将无法执行(将记录异常)。

References: 参考文献:


#5楼

Besides redefining console._commandLineAPI , there are some other ways to break into InjectedScriptHost on WebKit browsers, to prevent or alter the evaluation of expressions entered into the developer's console. 除了重新定义console._commandLineAPI ,还有其他一些方法可以在WebKit浏览器中闯入InjectedScriptHost,以防止或更改对开发者控制台中输入的表达式的求值。

Edit: 编辑:

Chrome has fixed this in a past release. Chrome在过去的版本中已修复此问题。 - which must have been before February 2015, as I created the gist at that time -必须是在2015年2月之前,因为我当时创建了要点

So here's another possibility. 所以这是另一种可能性。 This time we hook in, a level above, directly into InjectedScript rather than InjectedScriptHost as opposed to the prior version. 这次,我们将上一个级别直接连接到InjectedScript而不是与先前版本相对应的InjectedScriptHost中。

Which is kind of nice, as you can directly monkey patch InjectedScript._evaluateAndWrap instead of having to rely on InjectedScriptHost.evaluate as that gives you more fine-grained control over what should happen. 很好,因为您可以直接猴子打补丁InjectedScript._evaluateAndWrap而不必依赖InjectedScriptHost.evaluate因为这可以使您对应该发生的事情进行更细粒度的控制。

Another pretty interesting thing is, that we can intercept the internal result when an expression is evaluated and return that to the user instead of the normal behavior. 另一个非常有趣的事情是,我们可以在对表达式求值时截取内部结果,并将其返回给用户,而不是将其返回给用户。

Here is the code, that does exactly that, return the internal result when a user evaluates something in the console. 这是代码,正是这样做的代码,当用户在控制台中评估某些内容时,将返回内部结果。

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. e.g.;
           //{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);

It's a bit verbose, but I thought I put some comments into it 有点冗长,但我想我在其中添加了一些评论

So normally, if a user, for example, evaluates [1,2,3,4] you'd expect the following output: 因此,通常,例如,如果某个用户评估[1,2,3,4]您将期望获得以下输出:

在此处输入图片说明

After monkeypatching InjectedScript._evaluateAndWrap evaluating the very same expression, gives the following output: 在monkeypatching InjectedScript._evaluateAndWrap评估了完全相同的表达式之后,给出以下输出:

在此处输入图片说明

As you see the little-left arrow, indicating output, is still there, but this time we get an object. 如您所见,指示输出的小左箭头仍然存在,但是这次我们得到一个对象。 Where the result of the expression, the array [1,2,3,4] is represented as an object with all its properties described. 在表达式的结果中,数组[1,2,3,4]表示为对象,并描述了其所有属性。

I recommend trying to evaluate this and that expression, including those that generate errors. 我建议尝试对此表达式进行评估,包括那些会产生错误的表达式。 It's quite interesting. 这很有趣。

Additionally, take a look at the is - InjectedScriptHost - object. 另外,看看is - InjectedScriptHost对象。 It provides some methods to play with and get a bit of insight into the internals of the inspector. 它提供了一些可以使用的方法,并可以对检查器的内部结构有一些了解。

Of course, you could intercept all that information and still return the original result to the user. 当然,您可以截取所有这些信息,并且仍将原始结果返回给用户。

Just replace the return statement in the else path by a console.log (res) following a return res . 只需通过替换其他路径return语句console.log (res)之后的return res Then you'd end up with the following. 然后,您将得到以下结果。

在此处输入图片说明

End of Edit 编辑结束


This is the prior version which was fixed by Google. 这是Google修复的先前版本。 Hence not a possible way anymore. 因此,不再有可能。

One of it is hooking into Function.prototype.call 其中之一是挂接到Function.prototype.call

Chrome evaluates the entered expression by call ing its eval function with InjectedScriptHost as thisArg Chrome通过使用InjectedScriptHost作为thisArg call其eval函数来评估输入的表达式

var result = evalFunction.call(object, expression);

Given this, you can listen for the thisArg of call being evaluate and get a reference to the first argument ( InjectedScriptHost ) 鉴于此,您可以侦听正在evaluatecallthisArg ,并获取对第一个参数的引用( 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);  
        }
    };
}

You could eg throw an error, that the evaluation was rejected. 您可能会抛出一个错误,即评估被拒绝。

在此处输入图片说明

Here is an example where the entered expression gets passed to a CoffeeScript compiler before passing it to the evaluate function. 这是一个示例 ,其中输入的表达式在传递给evaluate函数之前先传递给CoffeeScript编译器。


#6楼

Netflix also implements this feature 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$$) {
    }
})();

They just override console._commandLineAPI to throw security error. 他们只是覆盖console._commandLineAPI引发安全错误。

发布了0 篇原创文章 · 获赞 3 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/p15097962069/article/details/105198543