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

IT技术 javascript facebook google-chrome-devtools
2021-01-23 00:32:37

很明显,由于最近的骗局,开发人员工具被人们利用来发布垃圾邮件,甚至用来“破解”帐户。Facebook 屏蔽了开发者工具,我什至无法使用控制台。

在此处输入图片说明

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

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

这怎么可能?

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

在此处输入图片说明

6个回答

我是 Facebook 的安全工程师,这是我的错。我们正在为一些用户测试它,看看它是否可以减缓一些攻击,这些攻击会诱使用户将(恶意)JavaScript 代码粘贴到浏览器控制台中。

需要明确的是:试图在客户端阻止黑客通常是一个坏主意这是为了防止特定的社会工程攻击

如果你最终进入了测试组并且对此感到恼火,抱歉。我试图让旧的选择退出页面(现在是帮助页面)尽可能简单,同时仍然足够吓人以阻止至少一些受害者。

实际代码与@joeldixon66 的链接非常相似我们的有点复杂,没有充分的理由。

Chrome 将所有控制台代码封装在

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

...所以该站点重新定义console._commandLineAPI为抛出:

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

这还不够(尝试一下!),但这是主要技巧。


尾声:Chrome 团队认为从用户端 JS 击败控制台是一个错误并修复了该问题,使该技术无效。之后,添加了额外的保护以保护用户免受 self-xss 的侵害

Chrome 确实进行了更新,但这个家伙再次进行了修复:kspace.in/blog/2014/06/21/...
2021-03-19 00:32:37
@n00b 该警告消息只是一个console.log.
2021-03-28 00:32:37
请不要因为某些用户的愚蠢而破坏开发人员工具。像这样的“解决方案”让我燃烧了一百万个太阳。
2021-04-03 00:32:37
@Alf,您的选择退出页面现在显示帮助页面,而无法关闭此保护。
2021-04-05 00:32:37
我认为谷歌需要发布一个“安全”版本的 Chrome,没有 DevTools,并强制任何自动更新的人切换到这个版本一次。任何真正注意到差异并需要 DevTools 的开发人员都应该下载“可怕的”版本。事实上,直接在下载页面上将它们标记为“可怕的”和“安全的”,并通过明确说明“你可能在这里是因为社会工程攻击告诉你下载可怕的版本;请不要让达尔文主义者对自己造成伤害做这个。” 上帝保佑你们 FB 开发者如此有创意!
2021-04-07 00:32:37

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

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

参考:

我无法让它在任何页面上触发它。一个更强大的版本可以做到:

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

console.log('test');

输出样式:JavaScript 控制台中的颜色

编辑思维@joeldixon66有正确的想法:从控制台禁用 JavaScript 执行« :: KSpace :::

很酷,但仍然覆盖相同window.console.log = function(){//empty}并使用console.log
2021-03-17 00:32:37

除了重新定义 之外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. 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]您会期望以下输出:

在此处输入图片说明

在 MonkeypatchingInjectedScript._evaluateAndWrap评估完全相同的表达式后,给出以下输出:

在此处输入图片说明

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

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

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

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

只需将 else 路径中的 return 语句替换为console.log (res)后面的 a return res然后你会得到以下结果。

在此处输入图片说明

编辑结束


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

其中之一是挂钩 Function.prototype.call

Chrome 通过call使用InjectedScriptHostas对其 eval 函数进行计算来评估输入的表达式thisArg

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

鉴于此,您可以监听thisArgcall存在evaluate,并获得了第一个参数的引用(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以引发安全错误。