Как Facebook отключает встроенные в браузер инструменты разработчика?


1686

Таким образом, очевидно, что из-за недавних мошенничеств люди используют средства разработчика для размещения спама и даже используют для взлома учетных записей. Facebook заблокировал инструменты разработчика, и я даже не могу использовать консоль.

Введите описание изображения здесь

Как они это сделали ?? В одном сообщении о переполнении стека утверждалось, что это невозможно , но Facebook доказал их неправоту.

Просто зайдите на Facebook и откройте инструменты разработчика, введите один символ в консоль, и появится это предупреждение. Неважно, что вы вставите, оно не будет выполнено.

Как это возможно?

Они даже заблокировали автозаполнение в консоли:

Введите описание изображения здесь


21
Просто для удовольствия: console.log = function () {}
tnt-rox

Нашли ли вы решение о том, как они заблокировали функцию автозаполнения в консоли
Акшай Хегде,

1
@AkshayHegde Это был побочный эффект, вызванный блокировкой любого выполнения кода от devtools.
Дерек 朕 會

@Derek 朕 會 功夫 не могли бы вы поделиться кодом
Акшай Хегде

Просто, к сожалению, он больше не заблокирован в Chrome.
Джон Лорд

Ответы:


2439

Я инженер по безопасности в Facebook, и это моя вина. Мы тестируем это для некоторых пользователей, чтобы увидеть, может ли это замедлить некоторые атаки, когда пользователи обмануты вставляют (вредоносный) код JavaScript в консоль браузера.

Просто чтобы быть ясно: попытка заблокировать хакеров на стороне клиента - это плохая идея в целом; это для защиты от конкретной атаки социальной инженерии .

Если вы попали в тестовую группу и вас это раздражает, извините. Я пытался сделать старую страницу отказа (теперь страницу помощи ) настолько простой, насколько это возможно, при этом оставаясь достаточно страшной, чтобы остановить хотя бы некоторых жертв.

Фактический код очень похож на ссылку @ joeldixon66 ; У нас это немного сложнее без веской причины.

Chrome оборачивает весь консольный код в

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

... так сайт переопределяет, console._commandLineAPIчтобы бросить:

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

Этого не достаточно (попробуйте!) , Но это главный трюк.


Эпилог: Команда Chrome решила, что победа над консолью от JS на стороне пользователя была ошибкой, и исправила проблему , сделав эту технику недействительной. После этого была добавлена ​​дополнительная защита для защиты пользователей от self-xss .


10
Chrome сделал обновление, но этот парень снова исправился
Роджер Гайрадж

3
@ Alf, ваша страница отказа теперь показывает страницу справки без какой-либо возможности отключить эту защиту.
arm.localhost

168
Пожалуйста, не ломайте инструменты разработчика из-за глупости некоторых пользователей. Подобные «решения» заставляют меня гореть миллионами солнц.
Джонатан Данлэп

85
Я думаю, что Google должен выпустить «Безопасную» версию Chrome, без DevTools, и заставить кого-либо при автоматических обновлениях переключаться на эту версию только один раз. Любой разработчик, который действительно замечает разницу и нуждается в DevTools, должен скачать «страшную» версию. На самом деле, пометьте их как «Страшные» и «Безопасные» непосредственно на странице загрузки и удержите дарвинистов от причинения себе вреда, прямо заявив, что «вы, вероятно, здесь, потому что атака социальной инженерии сказала вам загрузить версию Scary; сделай это." Да благословит вас Бог, разработчики FB, за то, что вы такие творческие!
MonkeyZeus

11
@ n00b Это предупреждающее сообщение просто console.log.
gcampbell

91

Я нашел сценарий с настройками консоли Facebook, используя инструменты разработчика Chrome. Вот скрипт с небольшими изменениями для удобства чтения. Я удалил биты, которые я не мог понять:

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

При этом автоматическое завершение консоли происходит молча, а операторы, введенные в консоли, не будут выполняться (исключение будет записано в журнал).

Ссылки:


48

Я не мог заставить это вызвать это на любой странице. Более надежная версия этого сделает это:

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

console.log('test');

Чтобы оформить вывод: Цвета в консоли JavaScript

Изменить мышление @ joeldixon66 имеет право идея: выполнение Отключить JavaScript из консоли «::: Kspace :::


крутой, но все же затем переопределите его window.console.log = function(){//empty}и используйте console.log
очень круто

32

Помимо переопределения console._commandLineAPI, есть несколько других способов проникнуть в InjectedScriptHost в браузерах WebKit, чтобы предотвратить или изменить оценку выражений, введенных в консоль разработчика.

Редактировать:

Chrome исправил это в прошлом выпуске. - что должно было быть до февраля 2015 года, когда я создал суть в то время

Так что вот еще одна возможность. На этот раз мы подключаемся, на уровень выше, напрямую, InjectedScriptа не InjectedScriptHostв отличие от предыдущей версии.

Это довольно приятно, так как вы можете напрямую использовать патч для обезьяны InjectedScript._evaluateAndWrapвместо того, чтобы полагаться на него, InjectedScriptHost.evaluateпоскольку это дает вам более детальный контроль над тем, что должно произойти.

Еще одна довольно интересная вещь заключается в том, что мы можем перехватить внутренний результат при вычислении выражения и вернуть его пользователю вместо нормального поведения.

Вот код, который делает именно это, возвращает внутренний результат, когда пользователь что-то оценивает в консоли.

var is;
Object.defineProperty(Object.prototype,"_lastResult",{
   get:function(){
       return this._lR;
   },
   set:function(v){
       if (typeof this._commandLineAPIImpl=="object") is=this;
       this._lR=v;
   }
});
setTimeout(function(){
   var ev=is._evaluateAndWrap;
   is._evaluateAndWrap=function(){
       var res=ev.apply(is,arguments);
       console.log();
       if (arguments[2]==="completion") {
           //This is the path you end up when a user types in the console and autocompletion get's evaluated

           //Chrome expects a wrapped result to be returned from evaluateAndWrap.
           //You can use `ev` to generate an object yourself.
           //In case of the autocompletion chrome exptects an wrapped object with the properties that can be autocompleted. e.g.;
           //{iGetAutoCompleted: true}
           //You would then go and return that object wrapped, like
           //return ev.call (is, '', '({test:true})', 'completion', true, false, true);
           //Would make `test` pop up for every autocompletion.
           //Note that syntax as well as every Object.prototype property get's added to that list later,
           //so you won't be able to exclude things like `while` from the autocompletion list,
           //unless you wou'd find a way to rewrite the getCompletions function.
           //
           return res; //Return the autocompletion result. If you want to break that, return nothing or an empty object
       } else {
           //This is the path where you end up when a user actually presses enter to evaluate an expression.
           //In order to return anything as normal evaluation output, you have to return a wrapped object.

           //In this case, we want to return the generated remote object. 
           //Since this is already a wrapped object it would be converted if we directly return it. Hence,
           //`return result` would actually replicate the very normal behaviour as the result is converted.
           //to output what's actually in the remote object, we have to stringify it and `evaluateAndWrap` that object again.`
           //This is quite interesting;
           return ev.call (is, null, '(' + JSON.stringify (res) + ')', "console", true, false, true)
       }
   };
},0);

Это немного многословно, но я подумала, что добавлю некоторые комментарии

Поэтому обычно, если пользователь, например, оценивает, [1,2,3,4]вы ожидаете следующий результат:

введите описание изображения здесь

После того, как monkeypatching InjectedScript._evaluateAndWrapоценивает то же самое выражение, выдает следующий вывод:

введите описание изображения здесь

Как вы видите, маленькая левая стрелка, обозначающая вывод, все еще там, но на этот раз мы получаем объект. Где результат выражения, массив [1,2,3,4]представлен как объект со всеми описанными его свойствами.

Я рекомендую попытаться оценить это и то выражение, в том числе те, которые вызывают ошибки. Это довольно интересно.

Кроме того, взгляните на is - InjectedScriptHost- объект. Он предоставляет некоторые методы, с которыми можно поиграть и получить представление о внутренностях инспектора.

Конечно, вы можете перехватить всю эту информацию и все равно вернуть исходный результат пользователю.

Просто замените оператор возврата в пути else console.log (res)следующим a return res. Тогда вы получите следующее.

введите описание изображения здесь

Конец Правки


Это предыдущая версия, которая была исправлена ​​Google. Следовательно, это больше невозможно.

Один из них подключается к Function.prototype.call

Chrome оценивает введенное выражение, используя callего функцию eval InjectedScriptHostкакthisArg

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

Учитывая это, вы можете слушать к thisArgиз callсуществ evaluateи получить ссылку на первый аргумент ( InjectedScriptHost)

if (window.URL) {
    var ish, _call = Function.prototype.call;
    Function.prototype.call = function () { //Could be wrapped in a setter for _commandLineAPI, to redefine only when the user started typing.
        if (arguments.length > 0 && this.name === "evaluate" && arguments [0].constructor.name === "InjectedScriptHost") { //If thisArg is the evaluate function and the arg0 is the ISH
            ish = arguments[0];
            ish.evaluate = function (e) { //Redefine the evaluation behaviour
                throw new Error ('Rejected evaluation of: \n\'' + e.split ('\n').slice(1,-1).join ("\n") + '\'');
            };
            Function.prototype.call = _call; //Reset the Function.prototype.call
            return _call.apply(this, arguments);  
        }
    };
}

Например, вы можете выдать ошибку, что оценка была отклонена.

введите описание изображения здесь

Вот пример, где введенное выражение передается компилятору CoffeeScript перед передачей его evaluateфункции.


25

Netflix также реализует эту функцию

(function() {
    try {
        var $_console$$ = console;
        Object.defineProperty(window, "console", {
            get: function() {
                if ($_console$$._commandLineAPI)
                    throw "Sorry, for security reasons, the script console is deactivated on netflix.com";
                return $_console$$
            },
            set: function($val$$) {
                $_console$$ = $val$$
            }
        })
    } catch ($ignore$$) {
    }
})();

Они просто отменяют console._commandLineAPIошибку безопасности.


24

Это на самом деле возможно, так как Facebook смог это сделать. Ну, не фактические инструменты веб-разработчика, а выполнение Javascript в консоли.

Смотрите это: Как Facebook отключает встроенные в браузер Инструменты разработчика?

Это действительно мало что даст, поскольку существуют другие способы обойти этот тип безопасности на стороне клиента.

Когда вы говорите, что это на стороне клиента, это происходит вне контроля сервера, поэтому вы ничего не можете с этим поделать. Если вы спрашиваете, почему Facebook все еще делает это, это не для безопасности, а для защиты обычных пользователей, которые не знают JavaScript, от запуска кода (который они не умеют читать) в консоли. Это часто встречается на сайтах, которые обещают услугу авто-лайкера или других ботов функциональности Facebook после того, как вы делаете то, что они просят вас сделать, где в большинстве случаев они дают вам кусочек javascript для запуска в консоли.

Если у вас не так много пользователей, как в Facebook, то я не думаю, что нужно делать то, что делает Facebook.

Даже если вы отключите JavaScript в консоли, запуск JavaScript через адресную строку все еще возможен.

введите описание изображения здесь

введите описание изображения здесь

и если браузер отключает JavaScript в адресной строке (когда вы вставляете код в адресную строку в Google Chrome, он удаляет фразу «javascript:»), вставка JavaScript в одну из ссылок с помощью элемента inspect все еще возможна.

Осмотрите якорь:

введите описание изображения здесь

Вставьте код в href:

введите описание изображения здесь

введите описание изображения здесь

введите описание изображения здесь

Суть в том, что проверка на стороне сервера должна выполняться в первую очередь, а затем - на стороне клиента.


11

Chrome сильно изменился с тех пор, как Facebook мог отключить консоль ...

По состоянию на март 2017 года это больше не работает.

Лучшее, что вы можете сделать, это отключить некоторые функции консоли, например:

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

Мой простой способ, но это может помочь для дальнейших вариаций на эту тему. Перечислите все методы и измените их на бесполезные.

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

5

Внутренне devtools внедряет имя IIFE getCompletionsна страницу, вызываемое при нажатии клавиши внутри консоли Devtools.

Глядя на источник этой функции , он использует несколько глобальных функций, которые могут быть перезаписаны.

Используя Errorконструктор, можно получить стек вызовов, который будет включен getCompletionsпри вызове Devtools.


Пример:

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


Это довольно аккуратно, но это также приводит к сбою страницы.
Дерек 朕 會 功夫

@Derek 朕 會 功夫 Единственный способ (я нашел), чтобы подавить дальнейший пользовательский ввод
samdd

Интересно, можно ли выдать ошибку вместо использования бесконечного цикла. Редактировать: Проверено, не работает.
Дерек 朕 會 功夫

@Derek 朕 會 功夫 в блоке try catch. Возможно, вы могли бы переопределить функции над блоком, но это предотвратило бы только автоматическое завершение (не оценку)
samdd

2

простое решение!

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

1
Как это отключить console.log()?
Red

1
console.log()больше не имеет значения, когда консоль постоянно очищается :)
Mohmmad Ebrahimi Aval

Это плохая идея. хакер может проследить со своего терминала и посмотреть все логи.
GFxJamal

4
и если вы
отметите

0

Я бы пошел по пути:

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

  },
  set: function() {

  }
});

-2

Это не мера безопасности для того, чтобы слабый код оставляли без присмотра. Всегда находите постоянное решение для слабого кода и правильно защищайте свои сайты, прежде чем применять эту стратегию

Насколько мне известно, лучшим инструментом было бы добавление нескольких файлов javascript, которые просто возвращают целостность страницы в нормальное состояние путем обновления или замены содержимого. Отключение этого инструмента разработчика не будет лучшей идеей, поскольку обход всегда ставится под сомнение, поскольку код является частью браузера, а не рендеринга сервера, поэтому его можно взломать.

Если у вас есть js file oneпроверка на <element>изменения важных элементов и js file twoиjs file three проверки того, что этот файл существует в период вы будете иметь полную целостность восстановить на странице в течение периода.

Давайте возьмем пример из 4 файлов и покажем, что я имею в виду.

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

Теперь это просто для того, чтобы показать, что он работает на всех файлах и тегах, а также

   #heading {
   background-color:red;
   }

Если вы соберете все эти файлы вместе и создадите пример, вы увидите функцию этой меры. Это предотвратит некоторые непредвиденные инъекции, если вы правильно его внедрите во все важные элементы вашего индексного файла, особенно при работе с PHP.

Почему я выбрал перезагрузку вместо возврата к нормальному значению для атрибута, так это то, что у некоторых злоумышленников уже есть другая часть веб-сайта, уже настроенная и готовая, и это уменьшает объем кода. Перезагрузка уберет всю тяжелую работу атакующего, и он, вероятно, пойдет играть куда-нибудь попроще.

Еще одно примечание: это может стать большим количеством кода, так что держите его в чистоте и не забудьте добавить определения того, где они принадлежат, чтобы облегчить редактирование в будущем. Также установите желаемое количество секунд, так как 1-секундные интервалы на больших страницах могут иметь серьезные последствия на старых компьютерах, которые могут использовать ваши посетители.


это ... это не очень хороший подход.
RozzA
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.