web-dev-qa-db-fra.com

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

Donc apparemment à cause des récentes escroqueries, les outils de développement sont exploités par des utilisateurs pour publier des spams et sont 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.

Enter image description here

Comment ont-ils fait ça ?? Un poste Stack Overflow a affirmé qu’il n’était pas possible , mais Facebook leur a prouvé le contraire.

Allez simplement sur Facebook et ouvrez les outils de développement, tapez un caractère dans la console et cet avertissement apparaît. Peu importe ce que vous mettez, cela ne sera pas exécuté.

Comment est-ce possible?

Ils ont même bloqué l'auto-complétion dans la console:

Enter image description here

1602
Derek 朕會功夫

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

Soyons clairs: essayer de bloquer les hackers côté client est une mauvaise idée en général; Il s’agit de protéger contre une attaque spécifique de l’ingénierie sociale .

Si vous vous retrouvez dans le groupe de test et que cela vous ennuie, désolé .J'ai essayé de rendre l'ancienne page de désinscription (maintenant page d'aide ) aussi simple que possible, tout en ayant suffisamment peur pour vous arrêter moins certains des victimes.

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

Chrome enveloppe tout le code de la console

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

... donc le site redéfinit console._commandLineAPI pour lancer:

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

Ceci est pas assez (essayez-le!) , mais c’est l’astuce principale.


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

2339
Alf

J'ai localisé le script de la console de Facebook à l'aide des outils de développement de Chrome. Voici le script avec des changements mineurs pour la lisibilité. J'ai enlevé les morceaux 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, la complétion automatique de la console échoue en mode silencieux tandis que les instructions saisies dans la console échouent (l’exception sera consignée).

Références:

78
Salman A

Je ne pouvais pas l'obtenir pour déclencher cela sur n'importe quelle page. Une version plus robuste de ceci 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ésactive l'exécution de JavaScript depuis la console «::: KSpace :::

38
Will

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

Modifier:

Chrome a résolu ce problème dans une version antérieure. - ce qui devait être avant février 2015, car j'ai créé Gist à ce moment-là

Alors, voici une autre possibilité. Cette fois-ci, nous accrochons un niveau supérieur directement dans InjectedScript plutôt que InjectedScriptHost par opposition à la version précédente.

Ce qui est un peu gentil, car vous pouvez directement monkey patch InjectedScript._evaluateAndWrap au lieu de devoir vous appuyer sur InjectedScriptHost.evaluate car cela vous donne un contrôle plus fin sur ce qui devrait arriver.

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

Voici le code, qui fait exactement cela, renvoie 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 prolixe, mais je pensais y mettre quelques commentaires

Ainsi, normalement, si un utilisateur, par exemple, évalue [1,2,3,4], vous attendez le résultat suivant:

enter image description here

Après le monkeypatching InjectedScript._evaluateAndWrap en évaluant la même expression, donne le résultat suivant:

enter image description here

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

Je recommande d'essayer d'évaluer ceci et cette expression, y compris ceux qui génèrent des erreurs. C'est assez intéressant.

De plus, jetez un coup d'œil à l'objet is - InjectedScriptHost - . Il fournit quelques méthodes pour jouer avec et obtenir un peu de perspicacité sur les éléments internes de l'inspecteur.

Bien sûr, vous pouvez intercepter toutes ces informations tout en renvoyant le résultat original à l'utilisateur.

Il suffit de remplacer l'instruction de retour dans le chemin else par un console.log (res) après un return res. Ensuite, vous vous retrouvez avec ce qui suit.

enter image description here

Fin de l'édition


Ceci est la version précédente 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 saisie en call en modifiant sa fonction eval avec InjectedScriptHost en tant que thisArg.

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

Compte tenu de cela, vous pouvez écouter la thisArg de call être evaluate et 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 pourriez par exemple jeter une erreur, que l'évaluation a été rejetée.

enter image description here

Voici un exemple où l'expression entrée est transmise à un compilateur CoffeeScript avant de la transmettre à la fonction evaluate.

28
Moritz Roessler

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 écrasent simplement console._commandLineAPI pour générer une erreur de sécurité.

22
Fizer Khan

Ceci est en fait possible depuis que Facebook a été capable de le faire… .. Eh bien, pas les 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 du côté client, cela se produit en dehors du contrôle du serveur, vous ne pouvez donc rien faire à ce sujet. Si vous demandez pourquoi Facebook le fait toujours, ce n’est pas vraiment pour la sécurité, mais pour protéger les utilisateurs normaux qui ne connaissent pas javascript du code en cours d’exécution (qu’ils ne savent pas lire) dans la console. C’est courant pour les sites qui promettent un service de rapprochement automatique ou d’autres bots fonctionnels de Facebook après avoir fait ce qu’ils vous demandent de faire, où, dans la plupart des cas, ils vous donnent une petite partie de javascript à exécuter dans la console.

Si vous n'avez pas autant d'utilisateurs que Facebook, alors 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, il est toujours possible d'exécuter JavaScript via la barre d'adresse.

 enter image description here

 enter image description here

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

Inspecter l'ancre:

 enter image description here

Collez le code dans href:

 enter image description here

 enter image description here

 enter image description here

La ligne de fond est la validation côté serveur et la sécurité doit être la première priorité, puis côté client après.

20
Jomar Sevillejo

Chrome a beaucoup changé depuis que facebook pouvait désactiver la console ... 

En mars 2017, cela ne fonctionne plus. 

Le mieux que vous puissiez faire est de désactiver certaines des 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(){};
}
7
Alpha2k

Mon moyen simple, mais il peut aider pour de nouvelles variations sur ce sujet . Liste toutes les méthodes et les modifier en inutile.

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

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

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

En utilisant le constructeur Error , il est possible d'obtenir la pile d'appels, qui inclura getCompletions lorsqu'elle sera 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);
});

3
samdd

une solution simple!

setInterval(()=>console.clear(),1500);
2

Je suivrais le chemin de:

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

  },
  set: function() {

  }
});
0
Zibri

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

Le meilleur outil, de loin selon mes connaissances, serait d’ajouter plusieurs fichiers javascript qui modifieraient simplement l’intégrité de la page à la normale en actualisant ou en remplaçant le contenu. Désactiver cet outil de développement ne serait pas la meilleure idée, car le contournement est toujours en cause, car le code fait partie du navigateur et n'est pas un rendu de serveur. Il pourrait donc être craqué. 

Si js file one vérifie les modifications de <element> sur les éléments importants et que js file two et js file three vérifient que ce fichier existe par période, la restauration de l’intégrité complète de celle-ci sera effectuée dans 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, ceci est juste pour montrer que ça marche aussi sur tous les fichiers et balises

   #heading {
   background-color:red;
   }

Si vous mettez tous ces fichiers ensemble et construisez l'exemple, vous verrez la fonction de cette mesure. Cela évitera 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 reload 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, ce qui diminue le montant de code. La recharge enlèvera tout le travail difficile de l'attaquant et celui-ci ira probablement jouer plus facilement.

Une autre note: Cela pourrait devenir beaucoup de code alors gardez-le propre et assurez-vous d’ajouter des définitions à leur lieu de résidence pour faciliter les modifications à l’avenir. Définissez également les secondes sur votre valeur préférée, car des intervalles d'une seconde sur de grandes pages peuvent avoir des effets dramatiques sur les ordinateurs plus anciens que vos visiteurs pourraient utiliser

0
user9374996