显然,由于最近的骗局,开发者工具被人们用来发布垃圾邮件,甚至被用来“黑客”账户。Facebook已经屏蔽了开发者工具,我甚至无法使用控制台。

他们是怎么做到的??一篇Stack Overflow的帖子声称这是不可能的,但Facebook已经证明他们错了。

只需转到Facebook并打开开发工具,在控制台中键入一个字符,就会弹出此警告。无论你投入什么,它都不会被执行。

这怎么可能?

他们甚至在控制台中阻止了自动完成:


当前回答

我使用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);

这样,当在控制台中键入的语句将无法执行时,控制台自动完成将以静默方式失败(将记录异常)。

参考文献:

对象定义属性对象.getOwnPropertyDescriptorChrome的console.log功能(用于格式化输出的提示)

其他回答

我的方法很简单,但它有助于这个主题的进一步变化。列出所有方法并将其更改为无用。

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

然而,更好的方法是禁止以任何有意义的方式打开开发人员工具

(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', ()=>{
        // do some extra code if needed or ...
        // maybe even delete the page, I still like to add redirect just in case
        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) { // user had to resume the script manually via opened dev tools 
            window.dispatchEvent(new Event('devtools-opened'));
        }else{
            window.dispatchEvent(new Event('devtools-closed'));
        }
        setTimeout(verifyConsole, 100);
    }
    verifyConsole();        
})();

在Firefox中,它没有做到这一点,因为Firefox是一个开发人员浏览器,我认为由于命令WEBGL_debug_render_info在Firefox中被弃用,将被删除。请使用RENDERER和错误Referrer Policy:对于跨站点请求,将很快忽略限制较少的策略,包括“降级时没有Referrer”、“跨源时源”和“不安全url”:https://static.xx.fbcdn.net/rsrc.php/v3/yS/r/XDDAHSZfaR6.js?_nc_x=Ij3Wp8lg5Kz.

这实际上是可能的,因为Facebook能够做到这一点。嗯,不是实际的web开发工具,而是在控制台中执行Javascript。

看看这个:Facebook如何禁用浏览器的集成开发工具?

但这真的没什么用,因为还有其他方法可以绕过这种类型的客户端安全性。

当你说它是客户端时,它发生在服务器的控制之外,所以你对此无能为力。如果你问Facebook为什么仍然这样做,这实际上不是为了安全,而是为了保护不懂javascript的普通用户,防止他们在控制台中运行代码(他们不知道如何读取)。这对于那个些承诺自动点赞服务或其他Facebook功能机器人的网站来说是很常见的,他们会在你们完成他们要求你们做的事情后,给你们一小段javascript,让你们在控制台中运行。

如果你没有Facebook那么多的用户,那么我认为没有必要做Facebook正在做的事情。

即使在控制台中禁用Javascript,也可以通过地址栏运行Javascript。

如果浏览器在地址栏禁用javascript,(当你在Google Chrome中将代码粘贴到地址栏时,它会删除短语“javascript:”)仍然可以通过inspect元素将javascript粘贴到其中一个链接中。

检查锚固件:

在href中粘贴代码:

底线是服务器端验证和安全性应该首先进行,然后再进行客户端验证。

除了重新定义控制台_命令行API,还有一些其他方法可以在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. 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);

这有点冗长,但我想我在其中添加了一些注释

因此,通常情况下,例如,如果用户对[1,2,3,4]求值,则会得到以下输出:

猴痘注射后_evaluateAndWrap对相同的表达式求值,给出以下输出:

如您所见,指示输出的小左箭头仍然存在,但这次我们得到了一个对象。在表达式的结果中,数组[1,2,3,4]表示为一个对象,并描述了其所有财产。

我建议尝试评估这个和那个表达式,包括那些产生错误的表达式。这很有趣。

此外,查看is-InjectedScriptHost-对象。它提供了一些方法来使用和深入了解检查器的内部。

当然,您可以截取所有这些信息,并仍然将原始结果返回给用户。

只需将else路径中的return语句替换为后跟return res的console.log(res)即可。

编辑结束


这是Google修复的先前版本。因此不再是一种可能的方式。

其中一个是挂接到Function.prototype.call

Chrome通过使用InjectedScriptHost作为thisArg调用其eval函数来计算输入的表达式

var result=evalFunction.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);  
        }
    };
}

例如,您可以抛出一个错误,即评估被拒绝。

下面是一个示例,输入的表达式在传递给求值函数之前被传递给CoffeeScript编译器。

我使用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);

这样,当在控制台中键入的语句将无法执行时,控制台自动完成将以静默方式失败(将记录异常)。

参考文献:

对象定义属性对象.getOwnPropertyDescriptorChrome的console.log功能(用于格式化输出的提示)