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

в 5:33, , рубрики: ajax, csrf, jquery, php, token, информационная безопасность, метки: , , , ,

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

Каков же механизм такой атаки? Суть заключается в выполнении запроса с другого сайта под авторизационными данными пользователя. Например, у нас есть действие удаления своего аккаунта example.com/login/dropme. Если защиты от 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 мы всегда попадаем на страниу example.com/profile?csrf_token=...
соответственно нам осталось вытащить с помощью js токен из get параметра.

Послесловие

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

Автор: piromanlynx

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js