W jaki sposób Facebook wyłącza zintegrowane narzędzia programistyczne przeglądarki?


1685

Najwyraźniej z powodu ostatnich oszustw ludzie używają narzędzi programistycznych do wysyłania spamu, a nawet do „hakowania” kont. Facebook zablokował narzędzia programistyczne i nie mogę nawet korzystać z konsoli.

Wpisz opis zdjęcia tutaj

Jak oni to zrobili? Jeden post przepełnienia stosu stwierdził, że nie jest to możliwe , ale Facebook udowodnił, że się mylą.

Wystarczy wejść na Facebooka i otworzyć narzędzia programistyczne, wpisać w konsoli jeden znak, a pojawi się to ostrzeżenie. Bez względu na to, co włożysz, nie zostanie wykonane.

Jak to jest możliwe?

Nawet zablokowali automatyczne uzupełnianie w konsoli:

Wpisz opis zdjęcia tutaj


21
Dla zabawy: console.log = function () {}
tnt-rox

Czy znalazłeś rozwiązanie, w jaki sposób zablokowali funkcję autouzupełniania w konsoli
Akshay Hegde

1
@AkshayHegde Był to efekt uboczny spowodowany blokowaniem wykonywania kodu z devtools.
Derek 朕 會 功夫

@Derek 朕 會 功夫 czy możesz udostępnić kod
Akshay Hegde

po prostu fyi, nie jest już blokowane w chromie.
John Lord

Odpowiedzi:


2437

Jestem inżynierem bezpieczeństwa na Facebooku i to moja wina. Testujemy to, aby niektórzy użytkownicy sprawdzili, czy może spowolnić niektóre ataki, w których użytkownicy są nakłaniani do wklejenia (złośliwego) kodu JavaScript do konsoli przeglądarki.

Żeby było jasne: próba zablokowania hakerów po stronie klienta jest ogólnie złym pomysłem ; ma to na celu ochronę przed konkretnym atakiem inżynierii społecznej .

Jeśli znalazłeś się w grupie testowej i cię to denerwuje, przepraszam. Starałem się, aby stara strona rezygnacji (teraz strona pomocy ) była tak prosta, jak to możliwe, a jednocześnie była wystarczająco przerażająca, aby powstrzymać przynajmniej niektóre ofiary.

Rzeczywisty kod jest podobny do linku @ joeldixon66 ; nasz jest nieco bardziej skomplikowany bez powodu.

Chrome otacza cały kod konsoli

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

... więc strona redefiniuje console._commandLineAPIrzut:

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

To nie wystarczy (spróbuj!) , Ale to główna sztuczka.


Epilog: zespół Chrome zdecydował, że pokonanie konsoli przez JS po stronie użytkownika było błędem i naprawił problem , powodując, że ta technika była nieprawidłowa. Następnie dodano dodatkową ochronę, aby chronić użytkowników przed self-xss .


10
Chrome dokonał aktualizacji, ale ten facet dokonał poprawki ponownie: kspace.in/blog/2014/06/21/…
Roger Gajraj

3
@Alf, strona rezygnacji pokazuje teraz stronę pomocy bez możliwości wyłączenia tej ochrony.
arm.localhost

168
Proszę nie łamać narzędzi programistycznych z powodu głupoty niektórych użytkowników. „Rozwiązania” takie jak ten powodują, że płonę wściekłością milionów słońc.
Jonathan Dunlap

85
Myślę, że Google musi wydać „bezpieczną” wersję Chrome, bez DevTools, i zmusić wszystkich do automatycznych aktualizacji, aby przełączyli się na tę wersję tylko raz. Każdy programista, który faktycznie zauważa różnicę i potrzebuje DevTools, powinien pobrać „przerażającą” wersję. W rzeczywistości oznacz je jako „Straszne” i „Bezpieczne” bezpośrednio na stronie pobierania i powstrzymaj Darwinian przed wyrządzeniem sobie krzywdy, wyraźnie stwierdzając, że „Prawdopodobnie jesteś tutaj, ponieważ atak socjotechniczny nakazał Ci pobrać Straszną wersję; nie rób tego Zrób to." Niech Bóg was błogosławi twórcom FB za bycie tak kreatywnym!
MonkeyZeus

11
@ n00b Ten komunikat ostrzegawczy to tylko console.log.
gcampbell

91

Zlokalizowałem skrypt kasowania na Facebooku za pomocą narzędzi programistycznych Chrome. Oto skrypt z niewielkimi zmianami w zakresie czytelności. Usunąłem bity, których nie mogłem zrozumieć:

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

Dzięki temu automatyczne uzupełnianie konsoli nie powiedzie się po cichu, a instrukcje wpisane w konsoli nie zostaną wykonane (wyjątek zostanie zarejestrowany).

Bibliografia:


48

Nie mogłem tego uruchomić na żadnej stronie. Bardziej niezawodna wersja tego by to zrobiła:

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

console.log('test');

Aby nadać styl wynikowi: Kolory w konsoli JavaScript

Edytuj Myślenie @ joeldixon66 ma właściwy pomysł: Wyłącz wykonywanie JavaScript w konsoli «::: KSpace :::


fajny, ale nadal zastępuj to samo window.console.log = function(){//empty}i użyj console.log
super fajny

32

Oprócz redefiniowania console._commandLineAPI, istnieją inne sposoby włamania się do InjectedScriptHost w przeglądarkach WebKit, aby zapobiec lub zmienić ocenę wyrażeń wprowadzonych do konsoli programisty.

Edytować:

Chrome naprawił to w poprzedniej wersji. - które musiały być przed lutym 2015 r., ponieważ wtedy stworzyłem sedno

Oto kolejna możliwość. Tym razem w hak, poziom wyżej, bezpośrednio InjectedScriptzamiast InjectedScriptHostw przeciwieństwie do wcześniejszej wersji.

Co jest całkiem miłe, ponieważ możesz bezpośrednio małpować łatkę InjectedScript._evaluateAndWrapzamiast polegać na niej, InjectedScriptHost.evaluateponieważ daje to bardziej szczegółową kontrolę nad tym, co powinno się stać.

Inną dość interesującą rzeczą jest to, że możemy przechwycić wynik wewnętrzny podczas oceny wyrażenia i zwrócić go użytkownikowi zamiast normalnego zachowania.

Oto kod, który właśnie to robi, zwraca wynik wewnętrzny, gdy użytkownik ocenia coś w konsoli.

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

To trochę gadatliwe, ale pomyślałem, że dodałem do niego kilka komentarzy

Więc normalnie, jeśli użytkownik, na przykład, ocenia, [1,2,3,4]można oczekiwać następujących danych wyjściowych:

wprowadź opis zdjęcia tutaj

Po monkeypatchingu InjectedScript._evaluateAndWrapoceniającym to samo wyrażenie, daje następujące dane wyjściowe:

wprowadź opis zdjęcia tutaj

Jak widzisz mała strzałka w lewo wskazująca wyjście, nadal tam jest, ale tym razem otrzymujemy obiekt. W przypadku wyniku wyrażenia tablica [1,2,3,4]jest reprezentowana jako obiekt ze wszystkimi opisanymi właściwościami.

Polecam spróbować ocenić to i to wyrażenie, w tym te, które generują błędy. To dość interesujące.

Dodatkowo spójrz na obiekt is - InjectedScriptHost- . Zapewnia kilka metod zabawy i pozwala uzyskać wgląd w wewnętrzne elementy kontrolera.

Oczywiście możesz przechwycić wszystkie te informacje i nadal zwrócić oryginalny wynik użytkownikowi.

Wystarczy zastąpić instrukcję return w ścieżce else console.log (res)następującą literą a return res. Wtedy skończysz z następującymi.

wprowadź opis zdjęcia tutaj

Koniec edycji


To jest poprzednia wersja, która została naprawiona przez Google. Dlatego nie jest już możliwy sposób.

Jeden z nich się zaczepia Function.prototype.call

Chrome ocenia wprowadzone wyrażenie, wprowadzając callfunkcję eval przy pomocy InjectedScriptHostasthisArg

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

Biorąc to pod uwagę, można nasłuchiwać thisArgod callistoty evaluatei uzyskać odniesienie do pierwszego argumentu ( 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);  
        }
    };
}

Możesz np. Zgłosić błąd, że ocena została odrzucona.

wprowadź opis zdjęcia tutaj

Oto przykład, w którym wprowadzone wyrażenie jest przekazywane do kompilatora CoffeeScript przed przekazaniem go do evaluatefunkcji.


25

Netflix również implementuje tę funkcję

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

Po prostu zastępują, console._commandLineAPIaby zgłosić błąd bezpieczeństwa.


24

Jest to faktycznie możliwe, ponieważ Facebook był w stanie to zrobić. Cóż, nie rzeczywiste narzędzia programistyczne, ale wykonanie Javascript w konsoli.

Zobacz: Jak Facebook wyłącza zintegrowane narzędzia programistyczne przeglądarki?

To naprawdę nie zrobi wiele, ponieważ istnieją inne sposoby obejścia tego rodzaju zabezpieczeń po stronie klienta.

Kiedy mówisz, że jest to po stronie klienta, dzieje się to poza kontrolą serwera, więc niewiele możesz z tym zrobić. Jeśli pytasz, dlaczego Facebook nadal to robi, to nie jest tak naprawdę dla bezpieczeństwa, ale dla ochrony zwykłych użytkowników, którzy nie znają javascript przed uruchomieniem kodu (który nie umie czytać) do konsoli. Jest to powszechne w witrynach, które obiecują usługę automatycznego polubienia lub inne boty z funkcjonalnością Facebooka po zrobieniu tego, o co cię proszą, w większości przypadków dają ci wycinek javascript do uruchomienia w konsoli.

Jeśli nie masz tylu użytkowników, co Facebook, nie sądzę, że trzeba robić to, co robi Facebook.

Nawet jeśli wyłączysz Javascript w konsoli, nadal możesz uruchomić JavaScript w pasku adresu.

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

a jeśli przeglądarka wyłącza javascript na pasku adresu, (po wklejeniu kodu do paska adresu w Google Chrome usuwa frazę „javascript:”) wklejanie javascript do jednego z linków poprzez element inspekcji jest nadal możliwe.

Sprawdź kotwicę:

wprowadź opis zdjęcia tutaj

Wklej kod w href:

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

wprowadź opis zdjęcia tutaj

Najważniejsze jest sprawdzenie po stronie serwera i bezpieczeństwo powinno być najpierw, a potem po stronie klienta.


11

Chrome bardzo się zmienił, odkąd Facebook mógł wyłączyć konsolę ...

Według stanu na marzec 2017 r. To już nie działa.

Najlepsze, co możesz zrobić, to wyłączyć niektóre funkcje konsoli, na przykład:

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

9

Mój prosty sposób, ale może pomóc w dalszych zmianach na ten temat. Wymień wszystkie metody i zmień je na bezużyteczne.

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

5

Devtools wewnętrznie wstrzykują IIFE o nazwie getCompletionsna stronę, wywoływaną po naciśnięciu klawisza w konsoli Devtools.

Patrząc na źródło tej funkcji , wykorzystuje kilka globalnych funkcji, które można zastąpić.

Za pomocą Errorkonstruktora można uzyskać stos wywołań, który będzie obejmował getCompletionswywołanie przez Devtools.


Przykład:

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


To całkiem fajne, ale powoduje także awarię strony.
Derek 朕 會 功夫

@Derek 朕 會 功夫 Jedyny sposób (znalazłem), aby ukryć dalsze wprowadzanie danych przez użytkownika
samdd

Zastanawiam się, czy możesz rzucić błąd zamiast używać nieskończonej pętli. Edycja: przetestowana, nie działa.
Derek 朕 會 功夫

@Derek 朕 會 功夫 jest w bloku catch catch. Prawdopodobnie możesz zastąpić funkcje nad blokiem, ale zapobiegnie to tylko automatycznemu uzupełnianiu (a nie ocenie)
samdd


0

Postąpiłbym tak:

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

  },
  set: function() {

  }
});

-2

Nie jest to środek bezpieczeństwa w przypadku pozostawienia słabego kodu bez nadzoru. Zawsze uzyskuj trwałe rozwiązanie słabego kodu i odpowiednio zabezpiecz swoje witryny przed wdrożeniem tej strategii

Według mojej wiedzy najlepszym narzędziem byłoby dodanie wielu plików javascript, które po prostu zmieniają integralność strony z powrotem na normalną, odświeżając lub zastępując zawartość. Wyłączenie tego narzędzia programistycznego nie byłoby najlepszym pomysłem, ponieważ pomijanie jest zawsze kwestionowane, ponieważ kod jest częścią przeglądarki, a nie renderowaniem serwera, dlatego może zostać złamany.

Nie powinno być js file onesprawdzanie <element>zmian na ważnych elementach i js file twooraz js file threesprawdzenie, czy istnieje ten plik za okres trzeba będzie pełna integralność przywrócić na stronie w terminie.

Weźmy przykład 4 plików i pokażemy ci, co mam na myśli.

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

Teraz ma to tylko pokazać, że działa również na wszystkich plikach i znacznikach

   #heading {
   background-color:red;
   }

Jeśli połączysz wszystkie te pliki i zbudujesz przykład, zobaczysz funkcję tego środka. Zapobiegnie to niektórym nieprzewidzianym wstrzyknięciom, jeśli poprawnie zaimplementujesz je we wszystkich ważnych elementach pliku indeksu, szczególnie podczas pracy z PHP.

Dlaczego zdecydowałem się na przeładowanie zamiast powrotu do normalnej wartości dla atrybutu, to fakt, że niektórzy atakujący mogą mieć skonfigurowaną i gotową inną część strony internetowej, co zmniejsza ilość kodu. Przeładowanie usunie całą ciężką pracę atakującego i prawdopodobnie pójdzie grać gdzieś łatwiej.

Kolejna uwaga: może to być dużo kodu, więc utrzymuj go w czystości i pamiętaj, aby dodać definicje do ich miejsca, aby ułatwić edycję w przyszłości. Ustaw także preferowaną liczbę sekund, ponieważ 1-sekundowe odstępy na dużych stronach mogą mieć drastyczne efekty na starszych komputerach, z których mogą korzystać użytkownicy


to ... to nie jest dobre podejście.
RozzA,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.