- PVSM.RU - https://www.pvsm.ru -

Защита ajax-приложения от Cross Site Request атак (CSRF)

Совсем недавно у меня появилась задача защитить web-приложение полностью построенное на ajax от CSRF-атак [1].

Каков же механизм такой атаки? Суть заключается в выполнении запроса с другого сайта под авторизационными данными пользователя. Например, у нас есть действие удаления своего аккаунта example.com/login/dropme. [2] Если защиты от CSRF атаки нет, мы можем на нужном нам сайте разместить тег:

<img src="http://example.com/login/dropme">

Сразу после того как пользователь зайдет на приготовленную нами страницу и подгрузит содержимое img, его аккаунт на example.com будет удален. О защите от этого я расскажу под катом.

Механизм атаки

Суть атаки заключается в установке тега, подгружающего контент по url с другого сайта. Таким тегом может быть img, script, link(для css), iframe и возможно другие, которые сразу не пришли мне в голову.
Защита ajax приложения от Cross Site Request атак (CSRF)

Стандартные способы защиты и чем они меня не устроили

Есть простой способ защиты: проверка HTTP_REFERER. Он меня не устроил потому как браузер в анонимном режиме может и не посылать этот заголовок. В таком случае все «анонимные» пользователи будут подвержены такой атаке.

Есть более продвинутый способ защиты: добавление токена к url и проверка токена при выполнении действия. Чем же меня это не устроило? Есть готовое приложение в котором уже более 100 ссылок на различные страницы и действия, они выполнены в коде в виде <a href="..."></a>, а не в виде вызова функции с передачей url, соответственно править придется более 100 мест. Есть риск что то забыть.

Решение

Решение было найдено быстро. Поскольку всё в приложении работает через ajax, мы можем добавлять в ajax-запрос заголовок с токеном. В нашем случае, на jQuery это делается так:

$.ajaxSetup({
        headers: {
            'X-Csrf-Token':token
        }
    });

Помимо ajax-запросов могут быть и открытия в новой вкладке, и самое важное — первый заход в приложение. Эти запросы не содержат токена, но мы должны их обработать. Для их обработки мы используем следующее решение. Если токена в запросе нет, отдавать следующий html-код:

<html>
  <body>
    <?php echo $loading_text; ?>
    <script type='text/javascript'> <?php /** check img and other left src tags **/ ?>
      if (parent.document.location.href == document.location.href){ <?php /** check iframe **/ ?>
        document.location.href='<?php echo $url; ?>';
      }
    </script>
  </body>
</html>

Здесь $url — это тот урл по которому был сделан запрос с подписанным в конце &csrf_token. Если такой код будет отдан в тег img, script, link — он выполнен не будет и злоумышленник не добьется цели. Если же код будет встроен в iframe то условие в if отсечет его выполнение.

Соответственно нам осталось научиться обрабатывать токен из параметра GET и отдавать его на клиентскую сторону для нашего заголовка. Собственно отдавать его не особенно и проблематично. После загрузки страницы, например example.com/profile [3] мы всегда попадаем на страниу example.com/profile?csrf_token=... [4]
соответственно нам осталось вытащить с помощью js токен из get параметра.

Послесловие

Всё что описано выше я организовал в библиотеку и выложил на github [5]. В той библиотеке есть несколько недоработок, но я исправлю их как только появиться время. Данная библиотека уже работает на реальном проекте.

Автор: piromanlynx


Сайт-источник PVSM.RU: https://www.pvsm.ru

Путь до страницы источника: https://www.pvsm.ru/php-2/8375

Ссылки в тексте:

[1] CSRF-атак: http://ru.wikipedia.org/wiki/%D0%9F%D0%BE%D0%B4%D0%B4%D0%B5%D0%BB%D0%BA%D0%B0_%D0%BC%D0%B5%D0%B6%D1%81%D0%B0%D0%B9%D1%82%D0%BE%D0%B2%D1%8B%D1%85_%D0%B7%D0%B0%D0%BF%D1%80%D0%BE%D1%81%D0%BE%D0%B2

[2] example.com/login/dropme.: http://example.com/login/dropme.

[3] example.com/profile: http://example.com/profile

[4] example.com/profile?csrf_token=...: http://example.com/profile?csrf_token=...

[5] github: https://github.com/piroman-lynx/lpg_csrf_token