Введение
UserJS предоставляет пользователям удобный и простой механизм модификации веб-страниц, именно благодаря этому многие пользователи автоматизирую свои действия с помощью UserJS, а иногда и обходят слабые системы защиты.
Больше всего от использования UserJS пользователями страдают браузерные онлайн-игры, многие из которых уже начали войну против UserJS. Так, например, в игре Travian используются поддельные скрытые веб-формы, которые иногда появляются вместе с обычными, UserJS-скрипты, написанные без учета этой особенности, ошибаются и отправляют данные через фэйковую форму, за что игрок немедленно получает наказание.
Хотелось бы сразу отметить, что бороться можно только с известными UserJS-скриптами, показанное решение не универсально и не может защитить от любого скрипта.
Сегодня я представляю на ваш суд свой метод борьбы с пользовательскими скриптами — MD5-хэширование с последующим сравнением. Основные действующие роли играют JavaScript и PHP.
Легенда
Предположим, у нас есть веб-сайт с простой формой авторизации, но пользователи повадились не вводить свой логин, а использовать UserJS-скрипт, который добавляет в форму дополнительную кнопку, нажатие на которую автоматически вводит их логин. Этот пример взят просто для наглядности, на месте формы авторизации может быть, например, форма отправки войск в онлайн-стратегии, а UserJS-скрипт — бот, автоматически рассылающий войска.
Исходные данные
Итак, наша форма выглядит так:
Пользователи используют следующий UserJS в браузере Opera:
Copy Source | Copy HTML
- window.opera.addEventListener('AfterEvent.load', function (e){
- if (e.event.target instanceof Document){
- e.preventDefault();
- document.forms[ 0].innerHTML+='<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">';
- }
- },false);
Или в FireFox для расширения GreaceMonkey:
Copy Source | Copy HTML
- // ==UserScript==
- // @name test1
- // @namespace test
- // @include http://localhost/anti-userjs/test1.php
- // ==/UserScript==
-
- document.forms[ 0].innerHTML+='<input type="button" onclick="document.forms[0].login.value=\'Login\';" value="AutoLogin">';
После включения этого скрипта пользователи получают такой вид формы авторизации:
Нажатие на кнопку «AutoLogin», как видно из исходных текстов, приводит к заполнению поля «login».
Попробуем запретить пользователем использование этого скрипта.
Вывод формы авторизации
Выводить форму авторизации мы будем из PHP-файла, обрамим форму тегом div, а также захешируем все данные внутри этого тега средствами PHP. Хеш сохраним в сессии для последующего контроля:
Copy Source | Copy HTML
- <div id="main">
- <?
- ob_start();
- ?>
- <form method="POST">
- Логин: <input name="login" type="text"><br>
- Пароль: <input name="password" type="password" ><br>
- <input name="md5" type="hidden"><br>
- <input value="Войти" type="submit">
- </form>
- <?
- $data=ob_get_contents();
- ob_end_flush();
- $data=strtr($data,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz');
- $data=preg_replace("/[ \t\n\r]/",'',$data);
- $md5=md5($data);
- $_SESSION['md5']=$md5;
- ?></div>
Пара слов про обработку содержимого — браузеры Opera и FireFox по-своему изгаляются над HTML-кодом, поэтому обработка его через JavaScript приводит к разным результатам. Так, Opera приводит все HTML-теги к заглавным буквам, а FireFox меняет параметры в тегах по стандарту. Поэтому важно валидно писать контролируемый HTML-код.
Для устранения различной обработки регистра тегов и переноса строк из содержимого удаляются все пробелы, табуляции и переносы строк, а также все английские буквы приводятся в нижний регистр.
Еще одно важное замечание — кодировка документа должна быть UTF-8, с CP1251 JavaScript в браузерах и PHP работаю по-разному.
Контроль через JavaScript
Итак, форма выведена, её контрольная сумма сосчитана, теперь нужно посчитать её со стороны пользователя и убедится, что вмешательства UserJS не было. Для этого мы добавляем событие на onLoad и вызываем JS-функцию:
Copy Source | Copy HTML
- >
- function checkuserjs(){
- var data=document.getElementsByTagName('div')[ 0].innerHTML;
- data=strtr(data,'ABCDEFGHIJKLMNOPQRSTUVWXYZ','abcdefghijklmnopqrstuvwxyz');
- data=data.replace(/[ \t\n\r]/g,'');
- document.forms[ 0].md5.value=md5(data);
- }
В ней я использовал несколько готовых функций — md5 и strtr, я не буду выкладывать их в статье, вы сможете найти их в приложенном файле.
Java-Script на стороне пользователя выполняет те же самые действия, что и PHP-скрипт выше, результат — хэш содержимого — заносится в форму.
Обработка на стороне сервера
Дело остается за малым — проверить полученные данные:
Copy Source | Copy HTML
- if ($_POST){
- if ($_POST['md5']!=$_SESSION['md5'])
- die ('Использование UserJS запрещено!');
- echo 'Форма отправлена';
- }
Таким образом мы отлавливаем недобросовестных пользователей (обратите внимание на индикатор GreaseMonkey):
Заключение
Указанный мной пример демонстрирует сам подход к запрещению использования UserJS, при разработке решений на его основе нужно понять основной момент — запретить использование любого UserJS не возможно, поэтому нужно отслеживать выкладываемые скрипты, затрагивающие наши сайты, и вырабатывать защиту от них. Так как делаются такие скрипты на энтузиазме, велик шанс, что новая версия скрипта, обходящая нашу защиту, если и выйдет, то не скоро. Тот же Травиан это демонстрирует — простейшая защита с фэйковыми формами введена пол-года назад, до сих пор нет новой версии UserJS-скриптов.
Приложение: anti-userjs.tar.gz 3708 bytes