Защита от CSRF

csrfCSRF является одной из самых распространенных уязвимостей в современных веб-приложениях. Это связано с недооценкой разработчиками степени угрозы CSRF-атак, что выражается в недостаточных мерах защиты против CSRF или вообще в полном их отсутствии. На самом деле, эффективно обезопасить свое веб-приложение от CSRF совсем не сложно: уже достаточно давно практикуется универсальный способ, о котором я хотел бы рассказать в этом посте. Кроме того, будут затронуты уже готовые реализации этого приема: CSRFx и csrf-magic.

Общие сведения

CSRF-атака использует функцию браузера автоматической отправки идентификатора сессии с каждым GET/POST-запросом к веб-приложению. Атакующий находит ссылки, которые могут выполнять определенные операции, причем, как правило, эти функции доступны только для привилегированных пользователей. Это могут быть операции перевода денег, перенаправления почты на другой электронный ящик, сброса пароля, покупки каких-либо товаров и многое другое. Попав на страницу, находящуюся под контролем атакующего, жертва невольно совершит переход по ссылке, уязвимой к CSRF, и выполнит операцию в контексте целевого веб-приложения. Обычно при CSRF-атаках используют тэг <img>, при этом источником изображения является уязвимая ссылка, по которой браузер автоматически перейдет, добавив заголовок Cookie. Примеры:

  • <img src=”http://bank/transfer?account=Vasya&amount=10000&for=Oleg”>
  • <img src=”http://mail/forward?mail=mymail@mymail”>
  • <img src=”http://site/resetpassword?user=admin&password=123″>
  • <img src=”http://cms/admin/delete?article=5″>

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

Защита

Двумя самыми распространенными заблуждениями касательно способов защиты от CSRF являются:

  • Использование POST вместо GET: на самом деле совершить POST-запрос также легко как и GET.
  • Реализация страниц подтверждения: данный подход лишь ненамного затрудняет процесс атаки – необходимо делать два запроса вместо одного (переход по ссылке для подтверждения операции и собственно выполнение самой операции)

Грамотная защита основывается на использовании случайного идентификатора, который генерируется для каждой операции и необходим для ее выполнения. Данный идентификатор также называют маркером доступа (token, nonce); он помогает эффективно различать запросы.

Алгоритм защиты:

  1. создаем случайный маркер доступа и добавляем его в массив $_SESSION
    <?php
    $token = md5(uniqid(mt_rand() . microtime()));
    $_SESSION['token'] = $token;
    ?>
  2. к каждой форме добавляем hidden-поле с идентификатором
    <input name="token" type="hidden" value="<?php echo $token ?>" />
  3. теперь при каждом запросе необходимо проверять корректность маркера доступа
  4. <?php
    $token = $_POST['token'];
    if($_SESSION['token'] == $token) {
    	$_SESSION['token'] = '';
    	/* some actions */
    } else {
    	die("404 Not Found!");
    }
    ?>

Существуют и готовые реализации этого метода для PHP, которые надстраиваются над веб-приложениями, как вручную, так и автоматически с помощью директивы auto_prepend_file.

CSRFx
URL: http://code.google.com/p/csrfx/

  • Необходима база данных (MySQL)
  • Обрабатывает как GET, так и POST-запросы
  • Работает только с PHP5
  • Не поддерживает AJAX-вызовы

csrf-magic
URL: http://csrf.htmlpurifier.org/

  • Работает без БД
  • Обрабатывает только POST-запросы
  • Работает как с PHP4, так и с PHP5
  • Имеет обработчик метода XMLHttpResponse – поддерживает AJAX-вызовы

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


Posted

in

by

Comments

12 responses to “Защита от CSRF”

  1. Best Avatar
    Best

    Не совсем согласен с тобой, имея XSS можно выдернуть token из формы, и отправить его post’ом по средствам AJAX.

  2. Best Avatar
    Best

    Вопрос снят))

  3. Best Avatar
    Best

    <input name=”token” type=”hidden” value=”” />
    Наверно должно было быть так:
    <input name=”token” type=”hidden” value=”” />
    Или хотя бы так:
    <input name=”token” type=”hidden” value=”” />

  4. Best Avatar
    Best

    Блин, срезался код.

    надо заменить на

    или на

  5. Best Avatar
    Best

    Да ё маё)) второй пункт алгоритма защиты поправь. value=. в php нет принта, или <?=, поэтому токен не выведется на экран.

  6. Raz0r Avatar

    спасибо, поправил

  7. Andrey Avatar
    Andrey

    Здрасти.
    Автор слышал чтонибуть о Sessionid?

  8. Raz0r Avatar

    Здравствуйте, Andrey. Попробуйте прочитать пост еще раз, более внимательно.

  9. […] защиты против CSRF с помощью токенов, о чем я писал в одном из предыдущих постов. Кроме того, возможна (точнее была возможна в связи с […]

  10. Vinc Avatar

    Зачем строка
    $_SESSION[‘token’] = ”;

    Зачем обнулять это, если понадобится ещё выводить формы?

  11. Vinc Avatar

    Или token меняется при каждом обращении к странице?

  12. Alex Avatar
    Alex

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

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.