Home » Javascript » How does Facebook disable the browser's integrated Developer Tools?

How does Facebook disable the browser's integrated Developer Tools?

Posted by: admin November 3, 2017 Leave a comment


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.

Enter image description here

How did they do that?? One Stack Overflow post claimed that it is not possible, but Facebook has proven them wrong.

Just go to Facebook and open up the developer tools, type one character into the console, and this warning pops up. No matter what you put in, it will not get executed.

How is this possible?

They even blocked auto-complete in the console:

Enter image description here


I’m a security engineer at Facebook and this is my fault. 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.

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; ours is a little more complicated for no good reason.

Chrome wraps all console code in

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

… so the site redefines console._commandLineAPI to throw:

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. Afterwards, additional protection was added to protect users from self-xss.


I located the Facebook’s console buster script using Chrome developer tools. 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 () {
            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).



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;


To style the output: Colors in JavaScript console

Edit Thinking @joeldixon66 has the right idea: Disable JavaScript execution from console « ::: KSpace :::


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 developers console.


Chrome has fixed this in a past release. – which must have been before February 2015, as i created the gist at that time

So here’s another possibilty. This time we hook in, a level above, directly into InjectedScript rather than InjectedScriptHost as opposed to the prior version.

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 finegrained control about what should happen.

Another pretty intersting thing is, that we can intercept the internal result when an expression is evaluated and return that to the user instead of the normal behaviour.

Here is the code, that does exactly that, return the internal result when a user evaluates something in the console.

var is;
       return this._lR;
       if (typeof this._commandLineAPIImpl=="object") is=this;
   var ev=is._evaluateAndWrap;
       var res=ev.apply(is,arguments);
       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)

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:

enter image description here

After monkeypatching InjectedScript._evaluateAndWrap evaluating the very same expression, gives the following output:

enter image description here

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.

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. It provides some methods to play with and get a bit of insight at 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. Then you’d end up with the following.

enter image description here

End of Edit

This is the prior version which was fixed by google. Henve not a possible way anymore.

One of it is hooking into Function.prototype.call

Chrome evaluates the entered expression by calling its eval function with InjectedScriptHost as thisArg

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)

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 e.g. throw an error, that the evaluation was rejected.

enter image description here

Here is an example where the entered expression gets passed to a CoffeScript compiler before passing it to the evaluate function.


Netflix also implements this feature

(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.


This is actually possible since Facebook was able to do it.
Well, not the actual web developer tools but the execution of Javascript in console.

See this: How does Facebook disable the browser's integrated Developer Tools?

This really wont do much though since there are other ways to bypass this type of client-side security.

When you say it is client-side, it happens outside the control of the server, so there is not much you can do about it. If you are asking why Facebook still does this, this is not really for security but to protect normal users that do not know javascript from running code (that they don’t know how to read) into the console. This is common for sites that promise auto-liker service or other Facebook functionality bots after you do what they ask you to do, where in most cases, they give you a snip of javascript to run in console.

If you don’t have as much users as Facebook, then I don’t think there’s any need to do what Facebook is doing.

Even if you disable Javascript in console, running javascript via address bar is still possible.

enter image description here

enter image description here

and if the browser disables javascript at address bar, (When you paste code to the address bar in Google Chrome, it deletes the phrase ‘javascript:’) pasting javascript into one of the links via inspect element is still possible.

Inspect the anchor:

enter image description here

Paste code in href:

enter image description here

enter image description here

enter image description here

Bottom line is server-side validation and security should be first, then do client-side after.


Chrome changed a lot since the times facebook could disable console…

As per March 2017 this doesn’t work anymore.

Best you can do is disable some of the console functions, example:

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(){};


My simple way, but it can help for further variations on this subject.
List all methods and alter them to useless.

  Object.getOwnPropertyNames(console).filter(function(property) {
     return typeof console[property] == 'function';
  }).forEach(function (verb) {
     console[verb] =function(){return 'Sorry, for security reasons...';};