Javascript-форум (https://javascript.ru/forum/)
-   Общие вопросы Javascript (https://javascript.ru/forum/misc/)
-   -   Безопасность клиентского кода. (https://javascript.ru/forum/misc/32739-bezopasnost-klientskogo-koda.html)

BallsShaped 28.10.2012 17:17

Безопасность клиентского кода.
 
Правильно ли я понимаю, что всякие браузерные расширения, дополнения и юзер-скрипты имеют полный доступ ко всему клиентскому коду? То есть, например, можно заменить метод XMLHttpRequest.prototype.send и совершенно прозрачно случать запросы?
Хотелось бы узнать, как с этим делом обстоит у разнах браузеров?

dmitriymar 28.10.2012 17:35

и даже скрипты подгружаемые с другого сайта
одинаково

BallsShaped 28.10.2012 17:47

Причем сначала выполняется код расширений, а затем код страницы, так?

DjDiablo 28.10.2012 17:52

незадумывался никогда.

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

Думаю отличить нативную функция от скиптовой труда не составит.
alert(alert);
alert(  function(){alert(100) });

var x=function(){alert(100)};

alert(x.toString().length);

if ( test(x,23) ) alert ('функцию подменили');
else alert('функция в порядке');

var x=function(){alert(100500)};

if ( test(x,23) ) alert ('функцию подменили');
else alert('функция в порядке');


function test(f,c){
    return f.toString().length!=c;
}

dmitriymar 28.10.2012 18:01

Цитата:

Сообщение от BallsShaped
Причем сначала выполняется код расширений, а затем код страницы, так?

ну и если так -ничего не помешает запустить вредоносный через таймаут

BallsShaped 28.10.2012 18:26

Цитата:

Сообщение от dmitriymar
ну и если так -ничего не помешает запустить вредоносный через таймаут

Так нет, меня как раз волнует, что можно изменить нативные методы перед выполнением страницы.
Цитата:

Сообщение от DjDiablo
Думаю отличить нативную функция от скиптовой труда не составит.

Наивный:
var replaceNativeFunction = new function (){
  var functionList = [],
      originalList = [];

  function replaceNativeFunction( object, methodName, newMethod ){
    functionList.push( newMethod );
    originalList.push( object[ methodName ] );
    object[ methodName ] = newMethod;
  };

  replaceNativeFunction.getOriginal = function ( method ){
    var index = functionList.indexOf( method );
    return originalList[ index ];
  };

  replaceNativeFunction( Function.prototype, "toString", function ToString(){
      if( this.toString == ToString )
        return replaceNativeFunction.getOriginal( ToString ).call( replaceNativeFunction.getOriginal( this ) );
      return this.toString();
    } ); 

  return replaceNativeFunction;    
};

replaceNativeFunction( window, "alert", function newAlert( message ){
  var originalAlert = replaceNativeFunction.getOriginal( newAlert );
  originalAlert( message );
  originalAlert( "Trololo!!!" );
} );

alert( alert.toString() );

devote 28.10.2012 18:27

Цитата:

Сообщение от DjDiablo
Думаю отличить нативную функция от скиптовой труда не составит.

тока ты забываешь что в разных браузерах разная длинна, например у меня в опере 22 символа а не 23. Да и это обойти не составит труда:
var x=function(){alert(100)};

var old=x, x=function(){alert(100500)};
x.toString = function() {
    return old.toString();
}

alert( x.toString() );
x();

DjDiablo 28.10.2012 18:32

есть идеи лучше ?
и кстатии devote

var x=function(){alert(100)};

var old=x, x=function(){alert(100500)};
x.toString = function() {
    return old.toString();
}
alert( x.toString );

Gvozd 28.10.2012 18:41

Навскидку все встроенные функции приводятся к строке вида
function send() {
    [native code]
}

С точностью до пробела, и возможно имени функции.
Проверяйте так.
Только используйте встроенный в Object метод toString
Хотя и его могут подменить

Но в случае подмены скрипта плагином, вы все равно навряд ли где-то найдете оригинальную функцию

devote 28.10.2012 20:23

Цитата:

Сообщение от Gvozd
С точностью до пробела

не согласен, опера например делает в одну строку, ИЕ тоже делает иначе.. да и вообще все браузеры по разному.

Цитата:

Сообщение от DjDiablo
и кстатии devote

кто же спорит, но обойти это не проблема в любом случае, и проверка через toString бредовая идея

devote 28.10.2012 20:25

Цитата:

Сообщение от DjDiablo
есть идеи лучше ?
и кстатии devote

var x=function(){alert(100)};

// ------- 
var old=x, x=function(){alert(100500)};
x.toString = function() {
    return old.toString();
}
x.toString.toString = x.toString;
// -------

alert( x.toString );
alert( x.toString.toString );
alert( x.toString.toString.toString );
alert( x );
x();

DjDiablo 28.10.2012 20:55

Чо ты завёлся, никто и не сомневается что могёш )))

и насчёт бредовых идей ты зря, есть такая штука как гипотеза. И более того с чего ты взял что все зловреды заменяют toString ? Cо временем научатся конечно, но пока им это нафик ненадо :)

кстатии ещё о гипотезах.
Данные мало украсть их же ещё и отослать нужно,
в связи с этом вопрос: можно ли посчитать количество запросов из браузера ? И если их количество отличается от того которое делает наш скрипт, вывести предупреждение что возможно данные уплыли ??? Сам пока невижу возможности это сделать :( .

Dim@ 28.10.2012 21:09

Цитата:

Сообщение от Gvozd
Навскидку все встроенные функции приводятся к строке вида
function send() {
    [native code]
}

насколько помнится по личному опыту в сафари ставиться ещё один пробел, вместо
function send() {
    [native code]
}

function send () {
    [native code]
}

devote 28.10.2012 21:38

Цитата:

Сообщение от DjDiablo
Чо ты завёлся, никто и не сомневается что могёш )))

кто завелся? чет такого не помню. А вообще я к тому что делать проверку через toString бессмысленно.

Цитата:

Сообщение от DjDiablo
И более того с чего ты взял что все зловреды заменяют toString ?

не понял к чему это?

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

Вот вам защита
function replace_method( context, method, fn ) {

    var old = context[ method ];

    if ( old.toString === Function.prototype.toString ) {
        fn.toString = function() { return old.toString() }
        fn.toString.toString = function() { return old.toString.toString() }
        fn.toString.toString.toString = fn.toString.toString;
    } else {
        fn.toString = old.toString;
    }

    return context[ method ] = fn;
}


// ================ проверка =====================

var x=function(){alert(100)};
x.toString = function() {
    return 'This is my lib method';
}

// ------- меняем метод -------
x = replace_method( window, 'x', function(){
    alert( 100500 );
});
// -------

alert( x.toString );
alert( x.toString.toString );
alert( x.toString.toString.toString );
alert( x.toString.toString.toString.toString );
alert( x.toString.toString.toString.toString.toString );
alert( x.toString.toString.toString.toString.toString.toString );
alert( x );
x();



// -------- второй вариант без пользовательского toString -----------

var y=function(){alert(200)};

// ------- меняем метод -------
y = replace_method( window, 'y', function(){
    alert( 200500 );
});
// -------

alert( y.toString );
alert( y.toString.toString );
alert( y.toString.toString.toString );
alert( y.toString.toString.toString.toString );
alert( y.toString.toString.toString.toString.toString );
alert( y.toString.toString.toString.toString.toString.toString );
alert( y );
y();


// ------- меняем метод -------
replace_method( window, 'alert', (function( org ) {
    return function(){
        org.call( this, ["Привет: "].concat( [].slice.call( arguments ) ) );
    }
})( window.alert ));
// -------

alert( y.toString );
alert( y.toString.toString );
alert( y.toString.toString.toString );
alert( y.toString.toString.toString.toString );
alert( y.toString.toString.toString.toString.toString );
alert( y.toString.toString.toString.toString.toString.toString );
alert( y );
y();

DjDiablo 28.10.2012 22:16

Третий концепт.
Я расматриваю это как разминку для ума)

Рассмотрю поэтапам.
передача данных, допустим обьект data
1) функция шифрует data, допустим превращает data в json, и дальше преобразовывает уже текст, допустим заменяет символы числами, а числа записывает задом наперёд. Это неважно сейчас совершенно, главное чтобы преобразования были обратимыми. И в шифровании недолжно быть ключа, просто трансформация текста по какому то алгоритму.

2) Происходит, передача XMLRequest, который переписан каким то плагином, уходит две копии закодированных данных, одна на наш серв, вторая подлому хацкеру.

3) На нашем серве, есть код который получив эту абракодабру, в обратном порядке выполняет действия и получает исходный json.

4) Хацкер получает цифробуквенную кракозябру, отчего произнося множество неприличных слов идёт ковырять шифровалку на наш сайт. Допустим он её расковыряет, и напишет дешифратор. Но если он шпиёнит за многими сайтами, то написать декодер для каждого сайта он не сможет.

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

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

Всё вышеописанное совершенно немешает забирать информацию из DOM или юзать куки

BallsShaped 28.10.2012 22:39

devote, твою "защиту" (в кавычках, потому что в контексте сабжа это скорее нападение) легко выявить, проверив свойсво "toString" у метода.
alert.hasOwnProperty( "toString" ) // должно быть false

Но вроде как уже очевидно, что нативный метод от переделанного никак не отличишь, если переделать Function.prototype.toString.

DjDiablo, дело не в хацкерах. Тут логика понятна: сам расширение поставил - сам дурак. У меня другая проблема: не дать пользователю самому (по его собственному желанию) влезть в логику приложения.

melky 28.10.2012 22:47

Цитата:

Сообщение от devote
а насчет toString вот небольшой метод, который легко заменит метод и фиг ты узнаешь по toString что это нативный или нет.

а если так попробовать?
// 1. создаём фрейм.
var frame = document.createElement("iframe");

// 2. тырим из него toString
var orig_toString = frame.contentWindow.Function.toString;

// 3. это объект, у которого заменён toString.
var myEvilObj = { toString: function () { return "WHAHAHA"; }  };

// 4. если результаты их действия одинаковы, то никто ничего не заменял.
var isReplaced = orig_toString.call(myEvilObj) !== myEvilObj.toString();

// и так же параноить со всеми методами :)

BallsShaped 28.10.2012 23:13

melky, инспектор элементов в firebug позволяет просматривать элементы внутри iframe на другом домене, что как бы намекает, что дополнение исполняется и в iframe.

DjDiablo 28.10.2012 23:14

Если отойти от темы скриптов, помешать желанию пользователя, то наверного максимум возможного, это святая троица
обфускация + локальные переменные функции + eval
<script>
codes='(function(){var x=100;alert(x);y=200;})()';
eval (codes);
</script>

И хрен вы сударь из консоли узнаете чему у вас Y равен, или x измените. Кстати никакой скрипт до этих переменных тоже недоберётся.

dmitriymar 28.10.2012 23:25

Вопрос к Т.С как много народа использует ваш сайт?
Цитата:

Сообщение от BallsShaped
что всякие браузерные расширения, дополнения и юзер-скрипты имеют полный доступ ко всему клиентскому коду?

как много народа поставит себе подобный функционал в браузер?

не будет ли в итоге 0.000000000000000000....1% вероятность ,чтоб заморачиватся на этом?
Да и есть другой момент-цель зловредов получить информацию почти во всех случаях, а не изменять методы браузера,и получить информацию помешать вы никак не сможете .
Так что вектор вашего вопроса, как минимум, не верный.

BallsShaped 28.10.2012 23:55

dmitriymar, давайте рассматривать тему, как чисто теоретическую. Если некоторая проблема, пусть сферическая и в вакууме, но которую я хочу решить. Назовем мой интерес чисто академическим:)

BallsShaped 29.10.2012 00:03

DjDiablo, не очень понимаю, причем тут eval, но обфускация и выполнение кода внутри функции - это само собой разумеющееся. Меня больше волнует то, что не скроешь. В частности, святая троица: пользовательский ввод + DOM + запросы.

DjDiablo 29.10.2012 00:09

Кстатии, раз уж внедрённые плагинами скрипты, это всётаки скрипты, то нельзя ли их выпилить со страницы силами javascript'а ?.
<!DOCTYPE HTML>
<html>
  <head> </head>
  <body>

    <script>
scr=document.getElementsByTagName('script');
console.log(scr);
//чото делаем с scr[0];
    </script>


  </body>
</html>


eval на отладку влияет. Выяснить где произошла ошибка, или или сделать в отладчике breakpoint, почти нереально.
<!DOCTYPE HTML>
<html>
  <head> </head>
  <body>
    <script>
     t="alert(1);";
     t+="alert(2);";
     t+="alert(" ;
       eval (t);  
    </script>
  </body>
</html>

melky 29.10.2012 00:11

Цитата:

Сообщение от BallsShaped
melky, инспектор элементов в firebug позволяет просматривать элементы внутри iframe на другом домене, что как бы намекает, что дополнение исполняется и в iframe.

это замечательно, но какое отношение это имеет глобальному объекту новосозданного окна, к которому имеет доступ только код в замыкании?

Цитата:

Сообщение от DjDiablo
Кстатии, раз уж внедрённые плагинами скрипты, это всётаки скрипты, то нельзя ли их выпилить силами со страницы силами javascript'а ?.

но evil-скрипт уже исполнился, каков результат будет от его удаления?

BallsShaped 29.10.2012 00:26

Цитата:

Сообщение от melky
какое отношение это имеет глобальному объекту новосозданного окна, к которому имеет доступ только код в замыкании?

Я не знаю, я просто предположил. Хотя, возможно, это все же решение. Скорее всего, пока фрейм не вставлен в dom, дополнения не исполняются. Проверить это я смогу не раньше, чем завтра.

devote 29.10.2012 01:06

Цитата:

Сообщение от melky
// и так же параноить со всеми методами :)

ну вот, хоть один разумный вариант)))

тока не:
frame.contentWindow.Function.toString
а вот:
frame.contentWindow.Function.prototype.toString

devote 29.10.2012 01:12

Цитата:

Сообщение от BallsShaped
Проверить это я смогу не раньше, чем завтра.

// 1. создаём фрейм.
var frame = document.createElement("iframe");
document.documentElement.appendChild( frame );
 
// 2. тырим из него toString
var Func_toString = frame.contentWindow.Function.prototype.toString; // для проверки функций
var Obj_toString = frame.contentWindow.Object.prototype.toString; // для проверки объектов
 
// 3. это объект, у которого заменён toString.
var myEvilObj = { toString: function () { return "WHAHAHA"; }  };

// 3.1. это функция у которой сменили
var x = function() {}
x.toString = function() { return "lalala" }
 
// 4. если результаты их действия одинаковы, то никто ничего не заменял.
alert( Obj_toString.call(myEvilObj) !== myEvilObj.toString() ); // true - заменили
alert( Func_toString.call(x) !== x.toString() ); // true - заменили


var y = function() {}
alert( Func_toString.call(y) !== y.toString() ); // false - не заменяли

melky 29.10.2012 01:21

Цитата:

Сообщение от devote
ну вот, хоть один разумный вариант)))

тока не:
frame.contentWindow.Function.toString
а вот:
frame.contentWindow.Function.prototype.toString

думал, что же лучше выбрать, но остановился на более коротком варианте

да и какая разница, собственно ? :)

alert(Function.prototype.toString === Function.toString);


Цитата:

Сообщение от devote (Сообщение 212799)
// 1. создаём фрейм.
var frame = document.createElement("iframe");
document.documentElement.appendChild( frame );
 
// 2. тырим из него toString
var Func_toString = frame.contentWindow.Function.prototype.toString; // для проверки функций
var Obj_toString = frame.contentWindow.Object.prototype.toString; // для проверки объектов
 
// 3. это объект, у которого заменён toString.
var myEvilObj = { toString: function () { return "WHAHAHA"; }  };

// 3.1. это функция у которой сменили
var x = function() {}
x.toString = function() { return "lalala" }
 
// 4. если результаты их действия одинаковы, то никто ничего не заменял.
alert( Obj_toString.call(myEvilObj) !== myEvilObj.toString() ); // true - заменили
alert( Func_toString.call(x) !== x.toString() ); // true - заменили


var y = function() {}
alert( Func_toString.call(y) !== y.toString() ); // false - не заменяли

если не изменяет память, нужно дождаться события load у фрейма ... нет? похоже, я уже сплю

devote 29.10.2012 02:07

Цитата:

Сообщение от melky
если не изменяет память, нужно дождаться события load у фрейма

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

devote 29.10.2012 05:14

Цитата:

Сообщение от iMaxmaxmaximus
Тред не читал

а стоило бы, что бы не иметь подобных фраз
Цитата:

Сообщение от iMaxmaxmaximus
безопасность нуливая


trikadin 29.10.2012 08:51

Цитата:

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

Тролль в треде. Будешь нести фигню - получишь добробан на пару дней.

DjDiablo 29.10.2012 12:36

Цитата:

Сообщение от devote
ну вот, хоть один разумный вариант)))

не говори ГОП пока не перепрыгнешь :)

Сорри но чем createElement или .appendChild от toString отличается ? что мешает подменить и их ?

<html>
<head>
</head>

<body>
        <!-- здесь прячется зловред -->
	<script>      

         //тело зловреда спрячем полностью в замыкании;
         (function(){ 

                 // метод crack общий для window и всех фреймов, 
                 // даже если frame создаст фрейм, это непоможет

 	         function crack(target){
	
	              var realCreateElement=target.document.createElement;
	
	
	               target.Function.prototype.toString=function(){
	                   return 'trololo';  
	               }
	
	               target.Object.prototype.toString=function(){
	                   return 'trololo';  
	               }
	
	               target.document.createElement=function(param){           
  	  	                 var result=realCreateElement.apply(target.document,arguments);
		           
		                 if (param== 'iframe'){
		                      target.document.documentElement.appendChild( result );
		                      crack(result.contentWindow);
		                }
		
		                return result;               
		         };
	         }
	
	         crack(window);
           })();
    </script>  


     <!-- А здесь мы обманули всех при помощи фрейма -->    
     <script>  
    
    	// 1. создаём фрейм.
      var frame = document.createElement("iframe");
      document.documentElement.appendChild( frame );
       
      // 2. тырим из него toString
      var Func_toString = frame.contentWindow.Function.prototype.toString; // для проверки функций
      var Obj_toString = frame.contentWindow.Object.prototype.toString; // для проверки объектов

        alert (Func_toString.apply("isu98") );
        alert (Obj_toString.apply("isu98") );

    </script>  
    

</body>

</html>

melky 29.10.2012 15:59

Цитата:

Сообщение от DjDiablo
Сорри но чем createElement или .appendChild от toString отличается ? что мешает подменить и их ?

мешает сеттер на innerHTML у элементов
var bad = function () { return "LOL"; }; // so sarcastic

// все они не помечены как readonly, но некоторые из них FF их менять не хочет.
// в хроме всё взламывается :)
document.createElement = Document.prototype.createElement = bad;
HTMLElement.prototype.insertAdjacentHTML = bad;

alert(document.createElement); // :((

// начинаем мстить.

// 1. получаем ГО - обычный window может прокатить : 
var win = window;

// window.document -> readonly!
// 2. получаем document.
var doc = win.document; // в doc - тёплый и мягкий экземпляр Document.

// document.body -> readonly!
// 3. получаем наконец-то элемент
var body = doc.body;

// 4. меняем innerHTML (надо бы менять не body'евский, а чей-нибудь другой)
//  и прокалываем фрейму ухо, чтобы можно было его опознать.
var id = "secret_" + (Math.random()*1e6|0);
body.innerHTML += '<iframe id="' + id + '"></iframe>';

// document.body.children -> readonly!
// 5. проходимся по детям и находим наш фрейм.
var myFrame;
for (var i = 0; i < body.children.length; i++) {
    if (body.children[i].id === id) {
        myFrame = body.children[i];
        break;
    }
}

// 6. PROFIT!!!
var normalWindow = myFrame.contentWindow;
var normalDocument = normalWindow.document;

alert( normalDocument.createElement );

devote 29.10.2012 16:35

Цитата:

Сообщение от DjDiablo
не говори ГОП пока не перепрыгнешь

а ты в курсе что у вставленного элемента появляется свойство parentNode?
var frame = document.createElement("iframe");
document.documentElement.appendChild( frame );
alert( frame.parentNode );

var frame = document.createElement("iframe");

// если он тут есть, значит метод createElement подменили
// тут можно смело останавливать программу и слать всех лесом.
alert( frame.parentNode );

DjDiablo 29.10.2012 17:01

Цитата:

Сообщение от devote
а ты в курсе

Да в курсе.
поэтому и написал что appendChild можно подменить тоже. В этом случае кряку применять appendChild самому нет никакой надобности. Достаточно дождаться пока жертва воспользуется нашей обёрткой думая что это appendChild, и подсунуть свои методы в фрейм.

А вот melkiy в ответ на критику алгоритм улучшил серьёзно. Появилось новое поле для размышлений.

nerv_ 29.10.2012 17:20

Цитата:

Сообщение от BallsShaped
Причем сначала выполняется код расширений, а затем код страницы, так?

кстати, так и не выяснили. А то можно было бы сделать какой-нибудь финт ушами :)
Object.freeze( Object.prototype );

Object.prototype.toString = function() {
	return false;
};

alert( {} );


Часовой пояс GMT +3, время: 04:56.