Como o Facebook desativa as Ferramentas de desenvolvedor integradas do navegador?

1685

Aparentemente, por causa dos golpes recentes, as ferramentas do desenvolvedor são exploradas pelas pessoas para postar spam e até usadas para "invadir" contas. O Facebook bloqueou as ferramentas de desenvolvedor, e eu não posso nem usar o console.

Digite a descrição da imagem aqui

Como eles fizeram isso? Um post do Stack Overflow afirmou que não é possível , mas o Facebook provou que eles estavam errados.

Basta ir ao Facebook e abrir as ferramentas do desenvolvedor, digite um caractere no console e esse aviso será exibido. Não importa o que você colocar, ele não será executado.

Como isso é possível?

Eles até bloquearam o preenchimento automático no console:

Digite a descrição da imagem aqui

Derek 朕 會 功夫
fonte
21
Apenas por diversão: console.log = function () {}
tnt-rox
Você achou solução para como eles bloquearam auto recurso completo em consola
Akshay Hegde
1
@AkshayHegde Foi um efeito colateral causado pelo bloqueio de qualquer execução de código nas devtools.
Derek # 28/19
@Derek 朕 會 功夫, você pode compartilhar o código #
Akshay Hegde
apenas para sua informação, não está mais bloqueado no chrome.
John Lord

Respostas:

2437

Sou engenheiro de segurança no Facebook e isso é culpa minha. Estamos testando isso para alguns usuários, para ver se é possível retardar alguns ataques nos quais os usuários são induzidos a colar código JavaScript (malicioso) no console do navegador.

Só para esclarecer: tentar bloquear hackers do lado do cliente é uma má ideia em geral; isso é para proteger contra um ataque específico de engenharia social .

Se você acabou no grupo de teste e está irritado com isso, desculpe. Tentei tornar a página de desativação antiga (agora página de ajuda ) o mais simples possível, apesar de ainda ser assustadora o suficiente para impedir pelo menos algumas das vítimas.

O código real é bastante semelhante ao link de @ joeldixon66 ; o nosso é um pouco mais complicado sem uma boa razão.

O Chrome agrupa todo o código do console

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

... então o site redefine console._commandLineAPIpara lançar:

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

Isso não é o suficiente (tente!) , Mas esse é o principal truque.


Epílogo: a equipe do Chrome decidiu que derrotar o console do JS do usuário era um bug e corrigiu o problema , tornando esta técnica inválida. Posteriormente, foi adicionada proteção adicional para proteger os usuários do self-xss .

Alf
fonte
10
O Chrome fez uma atualização, mas esse cara fez uma correção novamente: kspace.in/blog/2014/06/21/…
Roger Gajraj
3
@Alf, sua página de exclusão agora mostra a página de ajuda sem a possibilidade de desativar essa proteção.
arm.localhost
168
Por favor, não quebre as ferramentas do desenvolvedor por causa da estupidez de alguns usuários. "Soluções" como essa me fazem queimar com raiva de um milhão de sóis.
Jonathan Dunlap
85
Acho que o Google precisa lançar uma versão "Segura" do Chrome, sem o DevTools, e forçar alguém nas atualizações automáticas a mudar para esta versão apenas uma vez. Qualquer desenvolvedor que realmente perceba a diferença e precisa do DevTools deve baixar a versão "assustadora". De fato, identifique-os como "Assustadores" e "Seguros" diretamente na página de download e impeça os darwinianos de causar danos a si próprios, declarando explicitamente que "Você provavelmente está aqui porque um ataque de engenharia social disse para você baixar a versão assustadora; por favor, não faça isso." Deus te abençoe, devs do FB por serem tão criativos!
precisa saber é o seguinte
11
@ n00b Essa mensagem de aviso é apenas a console.log.
gcampbell
91

Eu localizei o script de buster de console do Facebook usando as ferramentas de desenvolvedor do Chrome. Aqui está o script com pequenas alterações para facilitar a leitura. Eu removi os bits que não consegui entender:

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

Com isso, o preenchimento automático do console falha silenciosamente enquanto as instruções digitadas no console falham na execução (a exceção será registrada).

Referências:

Salman A
fonte
48

Não consegui acioná-lo em nenhuma página. Uma versão mais robusta disso faria:

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

console.log('test');

Para estilizar a saída: Cores no console JavaScript

Editar O Thinking @ joeldixon66 tem a ideia certa: Desativar a execução do JavaScript no console «::: KSpace :::

Vai
fonte
arrefecer um, mas ainda, em seguida, substituir o mesmo window.console.log = function(){//empty}e uso console.log
super cool
32

Além de redefinir console._commandLineAPI, existem outras maneiras de invadir o InjectedScriptHost nos navegadores WebKit, para impedir ou alterar a avaliação das expressões inseridas no console do desenvolvedor.

Editar:

O Chrome corrigiu isso em uma versão anterior. - que deve ter sido antes de fevereiro de 2015, como eu criei a essência na época

Então aqui está outra possibilidade. Desta vez, conectamos um nível acima diretamente InjectedScriptao invés InjectedScriptHostda versão anterior.

O que é legal, pois você pode usar o patch diretamente em InjectedScript._evaluateAndWrapvez de precisar confiar, InjectedScriptHost.evaluatepois isso oferece um controle mais detalhado sobre o que deve acontecer.

Outra coisa bem interessante é que podemos interceptar o resultado interno quando uma expressão é avaliada e devolvê-lo ao usuário em vez do comportamento normal.

Aqui está o código, que faz exatamente isso, retorna o resultado interno quando um usuário avalia algo no 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);

É um pouco detalhado, mas pensei em colocar alguns comentários

Portanto, normalmente, se um usuário, por exemplo, avaliar [1,2,3,4]você esperaria a seguinte saída:

insira a descrição da imagem aqui

Após o monkeypatching InjectedScript._evaluateAndWrapavaliar a mesma expressão, fornece a seguinte saída:

insira a descrição da imagem aqui

Como você vê a pequena seta esquerda, indicando a saída, ainda está lá, mas desta vez obtemos um objeto. Onde o resultado da expressão, a matriz [1,2,3,4]é representada como um objeto com todas as suas propriedades descritas.

Eu recomendo tentar avaliar esta e aquela expressão, incluindo aquelas que geram erros. É bem interessante.

Além disso, dê uma olhada no objeto is - InjectedScriptHost- . Ele fornece alguns métodos para brincar e obter algumas informações sobre as partes internas do inspetor.

Obviamente, você pode interceptar todas essas informações e ainda assim devolver o resultado original ao usuário.

Apenas substitua a instrução de retorno no caminho else por um console.log (res)seguinte a return res. Então você terminaria com o seguinte.

insira a descrição da imagem aqui

Fim da Edição


Esta é a versão anterior que foi corrigida pelo Google. Portanto, não existe mais um caminho possível.

Uma delas é ligar Function.prototype.call

Chrome avalia a expressão introduzida pelo calling sua função eval com InjectedScriptHostcomothisArg

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

Diante disso, você pode ouvir o thisArgde callestar evaluatee obter uma referência para o primeiro argumento ( 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);  
        }
    };
}

Você poderia, por exemplo, lançar um erro, que a avaliação foi rejeitada.

insira a descrição da imagem aqui

Aqui está um exemplo em que a expressão inserida é passada para um compilador CoffeeScript antes de passá-la para a evaluatefunção.

Moritz Roessler
fonte
25

A Netflix também implementa esse recurso

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

Eles simplesmente substituem console._commandLineAPIpara gerar erro de segurança.

Fizer Khan
fonte
24

Isso é realmente possível, já que o Facebook conseguiu fazer isso. Bem, não as ferramentas atuais para desenvolvedores da Web, mas a execução do Javascript no console.

Veja o seguinte: Como o Facebook desativa as Ferramentas de desenvolvedor integradas do navegador?

Isso realmente não fará muito, já que existem outras maneiras de contornar esse tipo de segurança do lado do cliente.

Quando você diz que é do lado do cliente, isso acontece fora do controle do servidor, portanto não há muito o que fazer. Se você está perguntando por que o Facebook ainda faz isso, isso não é realmente por segurança, mas para proteger usuários normais que não sabem javascript de executar código (que eles não sabem ler) no console. Isso é comum em sites que prometem serviço de comparador automático ou outros bots de funcionalidade do Facebook depois que você faz o que eles pedem, onde, na maioria dos casos, eles fornecem um fragmento de javascript para execução no console.

Se você não tem tantos usuários quanto o Facebook, não acho necessário fazer o que o Facebook está fazendo.

Mesmo se você desabilitar o Javascript no console, ainda é possível executar o javascript pela barra de endereço.

insira a descrição da imagem aqui

insira a descrição da imagem aqui

e se o navegador desabilitar o javascript na barra de endereço, (quando você cola o código na barra de endereços no Google Chrome, ele exclui a frase 'javascript:') ainda é possível colar o javascript em um dos links por meio do elemento inspecionar.

Inspecione a âncora:

insira a descrição da imagem aqui

Cole o código no href:

insira a descrição da imagem aqui

insira a descrição da imagem aqui

insira a descrição da imagem aqui

A linha inferior é a validação do lado do servidor e a segurança deve ser a primeira, depois o lado do cliente.

Jomar Sevillejo
fonte
11

O Chrome mudou muito, pois os tempos em que o Facebook podia desativar o console ...

Em março de 2017, isso não funciona mais.

O melhor que você pode fazer é desativar algumas das funções do console, por exemplo:

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

Meu jeito simples, mas pode ajudar a mais variações sobre esse assunto. Liste todos os métodos e altere-os para inúteis.

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

Internamente, o devtools injeta um IIFE nomeado getCompletionsna página, chamado quando uma tecla é pressionada dentro do console do Devtools.

Observando a fonte dessa função , ele usa algumas funções globais que podem ser substituídas.

Usando o Errorconstrutor, é possível obter a pilha de chamadas, que incluirá getCompletionsquando chamada pelo Devtools.


Exemplo:

const disableDevtools = callback => {
  const original = Object.getPrototypeOf;

  Object.getPrototypeOf = (...args) => {
    if (Error().stack.includes("getCompletions")) callback();
    return original(...args);
  };
};

disableDevtools(() => {
  console.error("devtools has been disabled");

  while (1);
});

samdd
fonte
Isso é bem legal, mas também trava a página.
Derek
@Derek朕會功夫Única maneira (que eu encontrei) para suprimir ainda mais a entrada do usuário
samdd
Gostaria de saber se você pode lançar um erro em vez de usar um loop infinito. Edit: Testado, não funciona.
Derek
@Derek 功夫 會 功夫 está em um bloco de captura de tentativa. Você provavelmente poderia substituir as funções acima do bloco, mas ele só pode impedir a conclusão automática (não de avaliação)
samdd
2

uma solução simples!

setInterval(()=>console.clear(),1500);
Mohmmad Ebrahimi Aval
fonte
1
Como isso desabilita o console.log()?
Vermelho
1
console.log()não importa mais quando consola constantemente é limpo :)
Mohmmad Ebrahimi Aval
isso é uma péssima ideia. O hacker pode rastrear a partir de seu terminal e ver todos os logs.
GFxJamal
4
e se você sinalizar Preserve Logs, console.clear () não faz nada: P
Zibri
0

Eu seguiria o caminho de:

Object.defineProperty(window, 'console', {
  get: function() {

  },
  set: function() {

  }
});
Zibri
fonte
-2

Esta não é uma medida de segurança para que código fraco seja deixado sem supervisão. Sempre obtenha uma solução permanente para código fraco e proteja seus sites corretamente antes de implementar esta estratégia

A melhor ferramenta, de longe, de acordo com o meu conhecimento, seria adicionar vários arquivos javascript que simplesmente alteram a integridade da página de volta ao normal, atualizando ou substituindo o conteúdo. Desabilitar essa ferramenta de desenvolvedor não seria a melhor idéia, já que o desvio sempre está em questão, pois o código faz parte do navegador e não uma renderização do servidor; portanto, pode ser quebrado.

Caso você tenha js file onea verificação de <element>alterações em elementos importantes e js file twoe js file threeverificar se esse arquivo existir por período você terá a integridade restauração completa na página dentro do prazo.

Vamos dar um exemplo dos 4 arquivos e mostrar o que quero dizer.

index.html

   <!DOCTYPE html>
   <html>
   <head id="mainhead">
   <script src="ks.js" id="ksjs"></script>
   <script src="mainfile.js" id="mainjs"></script>
   <link rel="stylesheet" href="style.css" id="style">
   <meta id="meta1" name="description" content="Proper mitigation against script kiddies via Javascript" >
   </head>
   <body>
   <h1 id="heading" name="dontdel" value="2">Delete this from console and it will refresh. If you change the name attribute in this it will also refresh. This is mitigating an attack on attribute change via console to exploit vulnerabilities. You can even try and change the value attribute from 2 to anything you like. If This script says it is 2 it should be 2 or it will refresh. </h1>
   <h3>Deleting this wont refresh the page due to it having no integrity check on it</h3>

   <p>You can also add this type of error checking on meta tags and add one script out of the head tag to check for changes in the head tag. You can add many js files to ensure an attacker cannot delete all in the second it takes to refresh. Be creative and make this your own as your website needs it. 
   </p>

   <p>This is not the end of it since we can still enter any tag to load anything from everywhere (Dependent on headers etc) but we want to prevent the important ones like an override in meta tags that load headers. The console is designed to edit html but that could add potential html that is dangerous. You should not be able to enter any meta tags into this document unless it is as specified by the ks.js file as permissable. <br>This is not only possible with meta tags but you can do this for important tags like input and script. This is not a replacement for headers!!! Add your headers aswell and protect them with this method.</p>
   </body>
   <script src="ps.js" id="psjs"></script>
   </html>

mainfile.js

   setInterval(function() {
   // check for existence of other scripts. This part will go in all other files to check for this file aswell. 
   var ksExists = document.getElementById("ksjs"); 
   if(ksExists) {
   }else{ location.reload();};

   var psExists = document.getElementById("psjs");
   if(psExists) {
   }else{ location.reload();};

   var styleExists = document.getElementById("style");
   if(styleExists) {
   }else{ location.reload();};


   }, 1 * 1000); // 1 * 1000 milsec

ps.js

   /*This script checks if mainjs exists as an element. If main js is not existent as an id in the html file reload!You can add this to all js files to ensure that your page integrity is perfect every second. If the page integrity is bad it reloads the page automatically and the process is restarted. This will blind an attacker as he has one second to disable every javascript file in your system which is impossible.

   */

   setInterval(function() {
   // check for existence of other scripts. This part will go in all other files to check for this file aswell. 
   var mainExists = document.getElementById("mainjs"); 
   if(mainExists) {
   }else{ location.reload();};

   //check that heading with id exists and name tag is dontdel.
   var headingExists = document.getElementById("heading"); 
   if(headingExists) {
   }else{ location.reload();};
   var integrityHeading = headingExists.getAttribute('name');
   if(integrityHeading == 'dontdel') {
   }else{ location.reload();};
   var integrity2Heading = headingExists.getAttribute('value');
   if(integrity2Heading == '2') {
   }else{ location.reload();};
   //check that all meta tags stay there
   var meta1Exists = document.getElementById("meta1"); 
   if(meta1Exists) {
   }else{ location.reload();};

   var headExists = document.getElementById("mainhead"); 
   if(headExists) {
   }else{ location.reload();};

   }, 1 * 1000); // 1 * 1000 milsec

ks.js

   /*This script checks if mainjs exists as an element. If main js is not existent as an id in the html file reload! You can add this to all js files to ensure that your page integrity is perfect every second. If the page integrity is bad it reloads the page automatically and the process is restarted. This will blind an attacker as he has one second to disable every javascript file in your system which is impossible.

   */

   setInterval(function() {
   // check for existence of other scripts. This part will go in all other files to check for this file aswell. 
   var mainExists = document.getElementById("mainjs"); 
   if(mainExists) {
   }else{ location.reload();};
   //Check meta tag 1 for content changes. meta1 will always be 0. This you do for each meta on the page to ensure content credibility. No one will change a meta and get away with it. Addition of a meta in spot 10, say a meta after the id="meta10" should also be covered as below.
   var x = document.getElementsByTagName("meta")[0];
   var p = x.getAttribute("name");
   var s = x.getAttribute("content");
   if (p != 'description') {
   location.reload();
   }
   if ( s != 'Proper mitigation against script kiddies via Javascript') {
   location.reload();
   }
   // This will prevent a meta tag after this meta tag @ id="meta1". This prevents new meta tags from being added to your pages. This can be used for scripts or any tag you feel is needed to do integrity check on like inputs and scripts. (Yet again. It is not a replacement for headers to be added. Add your headers aswell!)
   var lastMeta = document.getElementsByTagName("meta")[1];
   if (lastMeta) {
   location.reload();
   }
   }, 1 * 1000); // 1 * 1000 milsec

style.css

Agora isso é apenas para mostrar que funciona em todos os arquivos e tags também

   #heading {
   background-color:red;
   }

Se você juntar todos esses arquivos e criar o exemplo, verá a função dessa medida. Isso evitará algumas injeções imprevistas, caso você a implemente corretamente em todos os elementos importantes do seu arquivo de índice, especialmente ao trabalhar com PHP.

O motivo pelo qual escolhi recarregar em vez de voltar ao valor normal por atributo é o fato de que alguns invasores podem ter outra parte do site já configurada e pronta e isso diminui a quantidade de código. A recarga removerá todo o trabalho duro do atacante e ele provavelmente irá jogar em algum lugar mais fácil.

Outra observação: isso pode se tornar um monte de código; portanto, mantenha-o limpo e adicione definições ao local a que pertencem para facilitar as edições no futuro. Defina também os segundos para sua quantidade preferida, pois intervalos de 1 segundo em páginas grandes podem ter efeitos drásticos em computadores mais antigos que seus visitantes possam estar usando


fonte
isso ... essa não é uma boa abordagem.
RozzA