Comment Facebook désactive-t-il les outils de développement intégrés du navigateur?

1685

Donc apparemment à cause des récentes escroqueries, les outils de développement sont exploités par les gens pour poster du spam et même utilisés pour "pirater" des comptes. Facebook a bloqué les outils de développement et je ne peux même pas utiliser la console.

Entrez la description de l'image ici

Comment ont-ils fait ça ?? Un article de Stack Overflow a affirmé que ce n'était pas possible , mais Facebook leur a prouvé le contraire.

Accédez simplement à Facebook et ouvrez les outils de développement, saisissez un caractère dans la console et cet avertissement apparaît. Peu importe ce que vous y mettez, il ne sera pas exécuté.

Comment est-ce possible?

Ils ont même bloqué la saisie semi-automatique dans la console:

Entrez la description de l'image ici

Derek 朕 會 功夫
la source
21
Juste pour le plaisir: console.log = function () {}
tnt-rox
Avez-vous trouvé une solution pour bloquer la fonctionnalité de
saisie semi-
1
@AkshayHegde C'était un effet secondaire causé par le blocage de toute exécution de code à partir des devtools.
Derek 朕 會 功夫
@Derek 朕 會 功夫 pouvez-vous s'il vous plaît partager le code
Akshay Hegde
juste pour info, il n'est plus bloqué en chrome.
John Lord

Réponses:

2438

Je suis ingénieur en sécurité chez Facebook et c'est ma faute. Nous testons cela pour certains utilisateurs pour voir si cela peut ralentir certaines attaques où les utilisateurs sont amenés à coller du code JavaScript (malveillant) dans la console du navigateur.

Juste pour être clair: essayer de bloquer les pirates côté client est une mauvaise idée en général; il s'agit de se protéger contre une attaque d'ingénierie sociale spécifique .

Si vous vous êtes retrouvé dans le groupe de test et que cela vous ennuie, désolé. J'ai essayé de rendre l'ancienne page de désinscription (maintenant la page d'aide ) aussi simple que possible tout en étant suffisamment effrayante pour arrêter au moins certaines des victimes.

Le code réel est assez similaire au lien de @ joeldixon66 ; la nôtre est un peu plus compliquée sans raison valable.

Chrome encapsule tout le code de la console dans

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

... donc le site redéfinit console._commandLineAPIpour lancer:

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

Ce n'est pas tout à fait suffisant (essayez-le!) , Mais c'est l'astuce principale.


Épilogue: L'équipe Chrome a décidé que la défaite de la console du côté utilisateur JS était un bug et a résolu le problème , rendant cette technique invalide. Par la suite, une protection supplémentaire a été ajoutée pour protéger les utilisateurs contre l'auto-xss .

Alf
la source
10
Chrome a fait une mise à jour mais ce gars a encore fait un correctif: kspace.in/blog/2014/06/21/…
Roger Gajraj
3
@Alf, votre page de désinscription affiche désormais la page d'aide sans possibilité de désactiver cette protection.
arm.localhost
168
Veuillez ne pas casser les outils de développement à cause de la stupidité de certains utilisateurs. Des "solutions" comme celle-là me font brûler de rage d'un million de soleils.
Jonathan Dunlap
85
Je pense que Google doit publier une version "sûre" de Chrome, pas de DevTools, et forcer quiconque sur les mises à jour automatiques à passer à cette version une seule fois. Tout développeur qui remarque réellement la différence et a besoin des DevTools doit télécharger la version "effrayante". En fait, étiquetez-les comme "Effrayant" et "Sécuritaire" directement sur la page de téléchargement et dissuadez les Darwiniens de se faire du mal en déclarant explicitement que "Vous êtes probablement ici parce qu'une attaque d'ingénierie sociale vous a dit de télécharger la version Effrayante; veuillez ne pas faites cela. " Que Dieu vous bénisse FB devs pour être si créatif!
MonkeyZeus
11
@ n00b Ce message d'avertissement n'est qu'un console.log.
gcampbell
91

J'ai localisé le script de désactivation de la console de Facebook à l'aide des outils de développement Chrome. Voici le script avec des changements mineurs pour la lisibilité. J'ai supprimé les bits que je ne pouvais pas comprendre:

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

Avec cela, l'auto-complétion de la console échoue silencieusement tandis que les instructions saisies dans la console ne s'exécuteront pas (l'exception sera consignée).

Références:

Salman A
la source
48

Je n'ai pas pu le déclencher sur n'importe quelle page. Une version plus robuste de cela le ferait:

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

console.log('test');

Pour styliser la sortie: Couleurs dans la console JavaScript

Edit Thinking @ joeldixon66 a la bonne idée: désactiver l'exécution JavaScript depuis la console «::: KSpace :::

Volonté
la source
cool one mais toujours remplacer la même chose window.console.log = function(){//empty}et utiliser console.log
super cool
32

Outre la redéfinition console._commandLineAPI, il existe d'autres façons de pénétrer dans InjectedScriptHost sur les navigateurs WebKit, pour empêcher ou modifier l'évaluation des expressions entrées dans la console du développeur.

Éditer:

Chrome a corrigé cela dans une version antérieure. - qui devait être avant février 2015, car j'ai créé l'essentiel à cette époque

Voici donc une autre possibilité. Cette fois, nous nous connectons, un niveau au-dessus, directement InjectedScriptplutôt que InjectedScriptHostpar opposition à la version précédente.

Ce qui est plutôt sympa, car vous pouvez directement corriger le singe InjectedScript._evaluateAndWrapau lieu de devoir vous y fier InjectedScriptHost.evaluatecar cela vous donne un contrôle plus précis sur ce qui devrait arriver.

Une autre chose assez intéressante est que nous pouvons intercepter le résultat interne lorsqu'une expression est évaluée et renvoyer cela à l'utilisateur au lieu du comportement normal.

Voici le code, qui fait exactement cela, retourne le résultat interne lorsqu'un utilisateur évalue quelque chose dans la 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);

C'est un peu bavard, mais je pensais y mettre quelques commentaires

Donc normalement, si un utilisateur, par exemple, évalue, [1,2,3,4]vous vous attendez à la sortie suivante:

entrez la description de l'image ici

Après InjectedScript._evaluateAndWrapévaluation monkeypatch de la même expression, donne la sortie suivante:

entrez la description de l'image ici

Comme vous le voyez, la petite flèche à gauche, indiquant la sortie, est toujours là, mais cette fois, nous obtenons un objet. Lorsque le résultat de l'expression, le tableau [1,2,3,4]est représenté comme un objet avec toutes ses propriétés décrites.

Je recommande d'essayer d'évaluer telle ou telle expression, y compris celles qui génèrent des erreurs. C'est assez intéressant.

De plus, jetez un œil à l' objet is - InjectedScriptHost- . Il fournit quelques méthodes pour jouer et obtenir un aperçu des éléments internes de l'inspecteur.

Bien sûr, vous pouvez intercepter toutes ces informations et renvoyer le résultat d'origine à l'utilisateur.

Remplacez simplement l'instruction return dans le chemin else par un console.log (res)a return res. Ensuite, vous vous retrouvez avec ce qui suit.

entrez la description de l'image ici

Fin du montage


Il s'agit de la version antérieure qui a été corrigée par Google. Ce n'est donc plus une voie possible.

L'un d'eux est accroché à Function.prototype.call

Chrome évalue l'expression entrée en calling sa fonction eval avec InjectedScriptHostasthisArg

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

Compte tenu de cela, vous pouvez écouter le thisArgd' callêtre evaluateet d' obtenir une référence au premier 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);  
        }
    };
}

Vous pouvez par exemple envoyer une erreur, que l'évaluation a été rejetée.

entrez la description de l'image ici

Voici un exemple où l'expression entrée est passée à un compilateur CoffeeScript avant de la passer à la evaluatefonction.

Moritz Roessler
la source
25

Netflix implémente également cette fonctionnalité

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

Ils remplacent simplement console._commandLineAPIpour lancer une erreur de sécurité.

Fizer Khan
la source
24

C'est en fait possible puisque Facebook a pu le faire. Eh bien, pas les véritables outils de développement Web, mais l'exécution de Javascript dans la console.

Voir ceci: Comment Facebook désactive-t-il les outils de développement intégrés du navigateur?

Cela ne fera vraiment pas grand-chose, car il existe d'autres moyens de contourner ce type de sécurité côté client.

Lorsque vous dites que c'est côté client, cela se produit en dehors du contrôle du serveur, donc vous ne pouvez pas faire grand-chose. Si vous demandez pourquoi Facebook continue de le faire, ce n'est pas vraiment pour la sécurité mais pour protéger les utilisateurs normaux qui ne connaissent pas javascript de l'exécution de code (qu'ils ne savent pas lire) dans la console. Cela est courant pour les sites qui promettent un service de recherche automatique ou d'autres robots de fonctionnalité Facebook après avoir fait ce qu'ils vous demandent de faire, où dans la plupart des cas, ils vous donnent un aperçu de javascript à exécuter dans la console.

Si vous n'avez pas autant d'utilisateurs que Facebook, je ne pense pas qu'il soit nécessaire de faire ce que Facebook fait.

Même si vous désactivez Javascript dans la console, l'exécution de javascript via la barre d'adresse est toujours possible.

entrez la description de l'image ici

entrez la description de l'image ici

et si le navigateur désactive le javascript dans la barre d'adresse, (lorsque vous collez du code dans la barre d'adresse dans Google Chrome, il supprime l'expression "javascript:"), coller du javascript dans l'un des liens via l'élément inspect est toujours possible.

Inspectez l'ancre:

entrez la description de l'image ici

Collez le code dans href:

entrez la description de l'image ici

entrez la description de l'image ici

entrez la description de l'image ici

L'essentiel est la validation côté serveur et la sécurité doit être la première, puis la côté client après.

Jomar Sevillejo
la source
11

Chrome a beaucoup changé depuis que Facebook a pu désactiver la console ...

En mars 2017, cela ne fonctionne plus.

Le mieux que vous puissiez faire est de désactiver certaines fonctions de la console, par exemple:

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
la source
9

Ma façon simple, mais cela peut aider à d'autres variations sur ce sujet. Dressez la liste de toutes les méthodes et modifiez-les comme inutiles.

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

En interne, devtools injecte un IIFE nommé getCompletionsdans la page, appelé lorsqu'une touche est enfoncée dans la console Devtools.

En regardant la source de cette fonction , il utilise quelques fonctions globales qui peuvent être écrasées.

En utilisant le Errorconstructeur, il est possible d'obtenir la pile d'appels, qui comprendra getCompletionslorsqu'elle est appelée par Devtools.


Exemple:

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
la source
C'est assez soigné, mais cela bloque également la page.
Derek 朕 會 功夫
@Derek 朕 會 功夫 Seul moyen (que j'ai trouvé) de supprimer d'autres entrées utilisateur
samdd
Je me demande si vous pouvez lancer une erreur au lieu d'utiliser une boucle infinie. Edit: testé, ne fonctionne pas.
Derek 朕 會 功夫
@Derek 朕 會 功夫 c'est dans un bloc try catch. Vous pourriez probablement remplacer les fonctions au-dessus du bloc, mais cela empêcherait uniquement la complétion automatique (pas l'évaluation)
samdd
2

une solution simple!

setInterval(()=>console.clear(),1500);
Mohmmad Ebrahimi Aval
la source
1
Comment cela désactive- console.log()t-il le ?
Red
1
console.log()n'a plus d'importance lorsque la console est constamment nettoyée :)
Mohmmad Ebrahimi Aval
C'est une mauvaise idée. le pirate peut tracer depuis son terminal et voir tous les journaux.
GFxJamal
4
et si vous marquez Preserve Logs, console.clear () ne fait rien: P
Zibri
0

J'irais dans le sens de:

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

  },
  set: function() {

  }
});
Zibri
la source
-2

Ce n'est pas une mesure de sécurité pour laisser du code faible sans surveillance. Obtenez toujours une solution permanente au code faible et sécurisez correctement vos sites Web avant de mettre en œuvre cette stratégie

D'après mes connaissances, le meilleur outil serait de loin d'ajouter plusieurs fichiers javascript qui rétablissent simplement l'intégrité de la page à la normale en actualisant ou en remplaçant le contenu. La désactivation de cet outil de développement ne serait pas la meilleure idée car le contournement est toujours en question car le code fait partie du navigateur et non d'un rendu de serveur, il pourrait donc être craqué.

Si vous devez js file onevérifier les <element>changements sur les éléments importants et js file twoetjs file three vérifier que ce fichier existe par période, vous aurez une restauration complète de l'intégrité sur la page pendant la période.

Prenons un exemple des 4 fichiers et montrons ce que je veux dire.

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

Maintenant, c'est juste pour montrer que cela fonctionne sur tous les fichiers et balises

   #heading {
   background-color:red;
   }

Si vous assemblez tous ces fichiers et construisez l'exemple, vous verrez la fonction de cette mesure. Cela empêchera certaines injections imprévues si vous l'implémentez correctement sur tous les éléments importants de votre fichier d'index, en particulier lorsque vous travaillez avec PHP.

La raison pour laquelle j'ai choisi de recharger au lieu de revenir à la valeur normale par attribut est le fait que certains attaquants pourraient avoir une autre partie du site Web déjà configurée et prête et cela réduit la quantité de code. Le rechargement supprimera tout le travail acharné de l'attaquant et il ira probablement jouer quelque part plus facilement.

Une autre remarque: cela pourrait devenir beaucoup de code, alors gardez-le propre et assurez-vous d'ajouter des définitions à l'endroit où elles appartiennent pour faciliter les modifications à l'avenir. Réglez également les secondes sur votre quantité préférée, car des intervalles de 1 seconde sur de grandes pages peuvent avoir des effets drastiques sur les anciens ordinateurs que vos visiteurs peuvent utiliser.


la source
ce ... ce n'est pas une bonne approche.
RozzA