Отсебятина Статьи Проекты
Облако тегов
Web CMS CSS htaccess HTML Javascript MySQL Php Безопасность Мониторы Новостная лента Оптимизация Ошибки Разработка сайта Часы Юзабилити оптимизация ошибки

Защита от подмены форм и накручивания голосований

Отсебятина от 16 ноября 2008 года.    Теги: Php Javascript HTML Безопасность


В интернете всегда найдутся конкуренты или простые кулцхакеры, которым будет хотеться вам напакостить. Причем напакостить сможет даже школьник, который открыл книгу по html и прочитал десяток страниц. Но, как известно, от большинства проблем можно избавиться. Как — расскажу в статье.

Подмена форм

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


Самой простой подмены форм можно избежать, если проверять приходящие данные на реферал:
Referer: http://kreker.org/items/5
Но недавно в логах своего сайта я обнаружил:
Referer: Field blocked by Outpost Firewall (http://www.agnitum.com)


Фаерволл пользователя блокирует реферы. Сколько таких пользователей? Поэтому вариант с рефералами отпадает, да и защищал-то он от самой бестолковой подмены.

Предлагаю вариант, который может защитить вас от подмены форм. Конечно, на 100% действенных вариантов не бывает, но эти помогут хоть как-то побороть проблему. Для примера я возьму голосовалку с тремя вариантами ответа. Итак, предположим, что у вас на сайте существует форма голосования:
<form method="post" action="">
Нравится ли вам мой сайт?
<input type="radio" name="true"> Да
<input type="radio" name="false"> Нет
<input type="radio" name="parallel"> Все равно
<input type="submit" name="send" value="Голосовать"> </form>

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

Злоумышленник вставляет вашу форму на свой сайт:
<form method="post" action="http://yoursite.ru/vote.php">
Нравится ли вам мой сайт?
<input type="radio" name="false"> Да
<input type="radio" name="false"> Нет
<input type="radio" name="false"> Все равно
<input type="submit" name="send" value="Голосовать"> </form>

От такой подмены легко защититься с помощью сессии:

<?php
session_start
();
.....
$_SESSION["voteid"] = true;
....
echo '<form method="post" action="">
Нравится ли вам мой сайт?
<input type="radio" name="true"> Да
<input type="radio" name="false"> Нет
<input type="radio" name="parallel"> Все равно
<input type="submit" name="send" value="Голосовать"> </form>';
А в vote.php:
<?php
session_start
();
if (!isset(
$_SESSION["voteid"])) {
die();
}
?>

Пользователь, проголосовавший с сайта злоумышленника увидит пустую страницу, а голос не учтется, потому что на моем сайте для него не была объявлена сессионная переменная "voteid".

Конечно, если на ваш сайт натравят бота, который будет постить в гостевой, при этом умея работать с сессиями, понадобиться более серьезная защита — не более одного поста в секунду с одного IP, не более 3х ссылок в сообщении, оценка сообщения на % ссылок и общего текста. Кстати, исскуственный запрос очень часто можно обнаружить по заголовку:

X-Powered-By: PHP/5.2.5


Защита формы голосования


Теперь представим, что статус голосования должен защищать его от ботов. Бот может уметь работать с сессиями. Конечно, от бота может спасти хорошая каптча, но, согласитесь, люди будут активно голосовать только в случае "тыкнул и забыл". Если ему предложат заполнять формы, то появляется огромная вероятность, что он уйдет со страницы.


Единственный вариант защитится от бота (который только умеет работать с сессиями и заголовками) — сделать голосование черным ящиком: формы должны изменять названия и меняться местами. Но есть вероятность, что бот сможет вычислить необходимый вариант по легенде "да" / "нет" / "Все равно", что напротив каждого варианта (input) голсования. Первая идея, которая приходит в голову - сделать картинку-фон, где будет отображена легенда (варианты ответов должны меняться местами), а в JS передавать координаты боксов с вариантами. Тогда боту надо будет научиться парсить ваш JS, распознавать картинку, определять необходимую область и связь с формой. Вроде бы неплохой вариант, сильно усложняющий жизнь боту, но слишком сложный. Подставлять текст через JS не катит — боту будет слишком легко нас пропарсить. Но деваться некуда и мы попробуем реализовать вариант через картинки.


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

Файл с самим голосованием - vote.php:
<?php
session_start
();
$variants = Array("true" => "Да", "false" => "Нет", "parallel" => "Все равно"); //Массив с вариантами ответов
if (isset($_POST["send"])) {
  if (!isset(
$_SESSION["vote_id"])) {
    die();
  }
  
$s_key = array_search("on", $_POST); //Выбираем имя Input, которое выбрал пользователь

  //Ниже ищем вариант ответа по имени input в сессии
  
$choise = null;
  foreach (
$_SESSION["vote"] as $key => $value) {
    if (
$value["name"] == $s_key) {
      
$choise = $key;
      break;
    }
  }
  if (
$choise == null) //Если вариант ответа не найден - значит форма подделана
    
die();
    
  echo
'Вы проголосовали  "'.$variants[$choise].'"';
}
else {
  
$_SESSION["vote_id"] = true;
  
$_SESSION["vote_fontsize"] = "10"; //размер текста на картинке
  
$_SESSION["vote"] = Array(
    
"true" => Array ("name" => md5(session_id()."true".rand())),
    
"false" => Array ("name" => md5(session_id()."false".rand())),
    
"parallel" => Array ("name" => md5(session_id()."parallel".rand()))
  );
//создаем в сессии массив с именами форм

  
$valuevars = sizeof($variants); //На будущее, когда кол-во вариантов будет меняться
  
$_SESSION["vote_text"] = "";
  
$random = array_rand($variants, $valuevars); //Рандомизируем массив P.S. shuffle не подойдет
  
for ($i = 0; $i <$valuevars; $i++) {
    
$_SESSION["vote_text"] .= $variants[$random[$i]]."\r\n";
    
$_SESSION["vote"][$random[$i]]["pos"] = $i*($_SESSION["vote_fontsize"]+3); //задаем позицию + 3 на отступы
  
}
  
//Для соли перемешаем формы отлично от картинки
  
$random = array_rand($_SESSION["vote"], $valuevars);
  
  
$inputs = "";
  for (
$i = 0; $i <$valuevars; $i++) {
    
$inputs .= '<input type="radio" name="'.$_SESSION["vote"][$random[$i]]["name"].'">
            <div class="variant" style="background-position: 0 -'
. $_SESSION["vote"][$random[$i]]["pos"] . 'pt"></div><br>'; //Рисуем формы
  
}
?>
<!-- В стилях -->
<style type="text/css">
  .variant {
    width: 100px;
    height: 10pt;
    background: url('voteimg.php') no-repeat;
    display: inline-block;
  }
</style>
<form method="post" action="">
Нравится ли вам мой сайт?<br>
<?php echo $inputs ?>
<input type="submit" name="send" value="Голосовать">
</form>
<?php
}
?>
Сразу предупреждаю: эта смесь php, html и css — пища для размышлений. Хоть он и корректно работает, но корректно отображается толко в FF3.

Файл с отрисовкой вариантов ответа - voteimg.php:
<?php
session_start
();
header('Content-type: image/gif');
header("Cache-Control: no-store, no-cache, must-revalidate");
header("Cache-Control: post-check=0, pre-check=0", false);
error_reporting(0);

//Установки папки со шрифтами
$fontpath = realpath('./fonts/');
putenv('GDFONTPATH='.$fontpath);

$text = iconv ("windows-1251", "UTF-8", $_SESSION["vote_text"]);

//Получаем координаты текстовой коробки
$coord = imagettfbbox(10, 0, "Verdana.ttf", $text);
$width = abs($coord[2] - $coord[6]);
$height = abs($coord[5] - $coord[1]);


//Создаем изображение размером с текстовую коробку
$image = imagecreatetruecolor($width, $height);
$color = imagecolorallocate($image, 0, 0, 0); //Здесь задаем цвет текста
$bgcolor = imagecolorallocate($image, 255, 255, 255); //Здесь задаем цвет фона
imageFilledRectangle($image, 0, 0, $width, $height, $bgcolor);
//Наносим текст
imagettftext($image, 10, 0, ($width - $coord[2]), ($height - $coord[1]), $color, "Verdana.ttf", $text);

//Выводим изображение
imagegif($image);
imagedestroy($image);
?>


Уникальность голосования


Данные, которые мы можем получить от пользователя (наиболее распространенные):
  • На PHP
    • IP-адрес
    • Тип и версия браузера
  • На JavaScript
    • Разрешение экрана
    • Глубина цветовой гаммы
    • Глубина пикселя (пиксель/дюйм)
    • Язык браузера
    • Типы файлов, поддерживаемые браузером
    • Плагины, установленные на браузер
    • Платформу клиента (тип ОС)


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

Как мы видим, нет ни одного уникального параметра (В слаборесурсных сетях для выхода в интернет используется шлюз с одним IP для выхода в интернет). Большинство из параметров имеют имеют всего с десяток распространенных значений, а некоторые и по 2-3. Еще есть возможность ставить куки, но их могут почистить или вообще отключить (на некоторых мобильных браузерах они не работают вовсе).


Необходимо найти комбинацию, которая даст возможность определить уникальность пользователя с большой вероятностью.
Что решено взять в комбинацию:
1) IP-адрес.
2) Браузер.
3) Разрешение экрана.
4) платформа ОС.
5) язык браузера
Дальше мы пойдем на "риск" — плагины для браузера. Под этими "плагинами" подразумеваются плагины-ридеры, например, Macromedia Flash Player, QuickTime.
Для каждого элемента мы зададим свой вес, чтобы не промазать, потому что некоторые вещи могут быть недоступны (например, список плагинов в ИЕ).

Конечно, все это очень смутно. Самое простое - использовать куки. Конечно, регистрация решает все проблемы, но вспомните:
Вы попадаете на сайт (не важно каким образом), пролистываете и видите голосование, в котором не прочь бы проголосовать, но с условием в 1 клик, а тут на тебе: "Зарегистрируйтесь"...
Таким образом пропадает очень много голосов.

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

Если у кого есть идеи — пишите
Аноним  23.05.2012 в 17:38
Защита от Подмена форм на сессиях непоможет если злоумышленник авторизирован у вас на сайте и накручивает рейтинг. НЕПОМОЖЕТ! :( Проверено.
Вот если НЕ авторизирован то возможно.
Аноним  23.05.2012 в 17:46
А конкреные примеры определения
Разрешение экрана
Глубина цветовой гаммы
Глубина пикселя (пиксель/дюйм)
Язык браузера
Типы файлов, поддерживаемые браузером
Плагины, установленные на браузер
Платформу клиента (тип ОС)
Неподскажите?
Kreker  06.01.2013 в 08:13
Найти клиентские данные можно в свойствах следующих объектов:
https://developer.mozilla.org/en-US/docs/DOM/window.screen
https://developer.mozilla.org/en-US/docs/DOM/window.navigator

По поводу подмены форм. 100% защиты не существует. Даже если голосование доступно только зарегистрировавшимся, то где гарантия, что бот не сделает 1000 регистраций?

На самом деле есть масса решений, помимо приведенных в этой статье. SSID могут действительно удалить и проголосовать снова.
Голосование можно ограничивать по IP. Если за минуту с одного IP два голоса - второй должен ввести каптчу.
Можно сделать голосование с участием Javascript, который будет помимо варианта ответа присылать какие-то обсчитанные данные (или в даже в RSA). Тогда бота-накрутчика придется изрядно доработать.
В любом случае, любая защита - вопрос времени. Но чем больше защит - тем дороже будет стоить бот. Главное, не перестараться и сохранить работоспособность голосовалки и всех пользователей.
Оставить сообщение






Любое копирование должно сопровождаться ссылкой на сайт.
Если вам что-то не понравилось — сообщайте.
Кича Владимир
x
Мне не нравится этот сайт, удалить его