Здравствуйте, уважаемые посетители cccp-blog.com! Наконец, после трёхмесячного перерыва я снова возвращаюсь к творчеству и продолжаю цикл публикаций по созданию корпоративного сайта на Laravel.
Так что тех из вас, кто думал, что я позабыл про свои обещания довести сайт до конца, спешу «обрадовать» 🙂 А тем из вас, кто попал впервые на данных блог, рекомендую подписаться на обновления, чтобы увидеть, чем данный цикл в итоге всё-таки закончится.
Итак, в предыдущей статье я показал вам как создавать Laravel controllers и правила роутинга для указания обработчиков запросов, посылаемых на определённые url сайта.
Там же я пообещал в будущих статьях рассказать об особенностях реализации AJAX в Laravel на примере создания контактной формы.
Реализации я в итоге решил посвятить следующую статью, а в данной сказать пару слов об особенностях, главной из которых является реализация в Laravel защиты от CSRF-атак, которая на практике слегка мешает и напрягает неподготовленных разработчиков.
Поэтому сегодня речь пойдёт о реализации Laravel CSRF protection на сайте, а также о легальных способах её обхода в исключительных случаях.
Поехали 🙂
Что такое CSRF
Те из вас, кто интересовался когда-то кибербезопасностью и безопасностью сайтов, в частности, наверняка знаком термин с аббревиатурой CSRF, которая расшифровывается как «Cross Site Request Forgery».
Если вы знаете, что это, то просто можете пропустить первые три блока статьи. Для тех же, что не в курсе, сделаю небольшое пояснение.
Википедия, опуская дословный перевод, говорит о том, что CSRF — это межсайтовая подделка запроса, которая является одним из видов атак на посетителей веб-сайтов.
Она заключается в том, что хакеры, используя данные залогиненных пользователей в куках различных сайтов (начиная от банальных блогов, заканчивая сайтами банков), производят от их лица различные действия. Спрашивается, как хакеры узнают данные пользователей, чтобы отправить запрос от их имени?
Самое интересное, что ничего они не узнают 🙂 Пользователи сами же дают согласие на произведение действий, угодных хакерам.
Как работает CSRF
Всем вам, наверное, неоднократно приходили на почту письма с содержанием вида «Смотри, какие милые котики» или «Не думал, что ты такое способен. Смотри, что я нашёл» и какими-то левыми ссылками с зашифрованными url и текстом, побуждающим по ним перейти.
При CSRF атаке после перехода по такой ссылке отправляется запрос на атакуемый ресурс — банка, блога или другой сайт с необходимыми параметрами, содержащие текст комментария, размер денежного перевода хакеру и т.д. И если вы будете залогинены на сайте, а атакуемый сайт будет хранить данные залогиненных пользователей в куках клиентских браузеров, то такой запрос будет с лёгкостью обработан.
В результате у вас со счёта уйдут деньги в неизвестном направлении или появится комментарий с абсолютно произвольным содержанием на блоге.
Ну а что? Всё правильно. После обращения к администрации сайта люди там будут мягко говоря обескуражены. Действие от авторизованного в системе пользователя было, но он при этом утверждает, что ничего не делал. Мистика какая-то 🙂
По логам атакуемого сайта будет видно, что пользователь сам отправил запрос, как бы он потом не доказывал, что это прошло случайно и его обманули.
Запрос прошёл проверку авторизации пользователя на сайте? Прошёл.
Получатель платежа и сумма были указаны? Были.
Даже IP адрес, с которого был произведён запрос, будет ваш же, т.к. вы сами нажали на ссылку.
Как уберечься от CSRF
Отсюда мораль: как пользователь, ни в коем случае не переходите ни по каким сомнительным ссылкам, особенно из писем в папке «Спам». Ну, а как разработчик, вы должны учитывать возможность данных атак на пользователей создаваемых вами сайтов и всячески их предотвращать.
Во-первых, самое простое, ни в коем случае не хранить данные авторизаций в куках браузера (для этого есть сессии на стороне сервера).
Во-вторых, важные действия (переводы денег, комментарии и т.д.) производить с помощью POST, PUT, DELETE, PATCH HTTP запросов, но никак не GET, HEAD, OPTIONS и TRACE. Так их сложнее будет подделать, т.к. отправить такой запрос с IP пользователя путём клика на ссылку не получится, адрес уже будет компьютера хакера, с которого он будет отправлять запрос, по которому его можно будет и вычислить (если он, конечно, не додумается его изменить или отправить запрос через прокси).
И, собственно говоря, вторая группа запросов предназначена для получения данных сайта, а не для изменения состояния сервера.
Ну, и третье, лучше при разработке пользоваться наработками опытных программистов, а лучше целых их команд, где обязательно должны быть специалисты по кибербезопасности.
Защита от CSRF в Laravel
К счастью, Laravel соответствует всем указанным выше требованиям. Поэтому, выбрав его, вы можете быть уверены, что данные ваших пользователей будут в полной безопасности. Осталось только научиться пользоваться методами Laravel CSRF protection.
А они достаточно просты и заключаются в указании CSRF токена при отправке запроса, который сверяется на сервере перед его обработкой.
CSRF token в Laravel представляет собой строку из 40 случайных символов (судя по коду Laravel 5.4, которым я в данный момент располагаю), который генерируется при создании новой сессии приложения, т.е. при активности нового пользователя на сайте.
Таким образом, проверка CSRF токена перед обработкой запроса позволяет выяснить, что данный запрос был отправлен конкретным пользователем, а не кем-то вместо него.
Если вы отправите запрос, скажем, через HTML форму методом POST, PUT, DELETE или PATCH, то получите следующую ошибку проверки CSRF:
Чтобы запрос отправился, необходимо в форму, с помощью которой отправляется запрос, добавить скрытое поле со значением CSRF token с помощью специальной функции-хэлпера csfr_field следующим образом:
<form method="post"> {{ csrf_field() }} ... </form>
Кстати, данный хэлпер появился сравнительно недавно — в Laravel 5.1. Так что, если вы используете более раннюю версию, то можете воспользоваться следующей конструкцией вместо предложенной:
<input type="hidden" name="_token" value="{!! csrf_token() !!}">
Также разработчики Laravel рекомендуют для дополнительной защиты AJAX запросов добавлять CSRF токен ко всем запросам на уровне JavaScript путём указания токена в значении метатэга, размещённого в секции head HTML-документа:
<meta name="csrf-token" content="{{ csrf_token() }}">
Считывание данного значения в JS производится следующей JQuery-конструкцией:
$.ajaxSetup({ headers: { 'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content') } });
При отправке же GET запроса для успешной его обработки сервером CSRF token указывать не нужно. Но, как известно, данный тип запроса и так считается небезопасным благодаря указанию параметров в URL запроса, которые можно увидеть в браузере. Поэтому пользоваться им для передачи конфиденциальных и важных данных не то, что не рекомендуется, а запрещается 🙂
Кстати, текущий CSRF токен указывается в качестве значения переменной COOKIE X-XSRF-TOKEN, которая присутствует в каждом ответе сервера. Поэтому иногда его будет удобнее использовать, особенно в ситуациях, когда значение CSRF токена необходимо получить в JavaScript, где хэлперы Laravel, как известно, не работают.
Обход CSRF защиты
Иногда возникают ситуации, когда запрос нужно не проверять на наличие в его параметрах CSRF токена. Особенно это актуально при работе с различными сервисами по API, т.к. ресурсы не будут знать, какой CSRF токен отправить при посылке ответа Laravel сайту.
За чтение CSRF token и его проверку перед выполнением запроса ответственны, как ни странно, Laravel middleware, а именно VerifyCsrfToken.php, который и генерирует исключение (exception) при попытке отправить POST запрос без указания токена.
Мы можем им воспользоваться в случаях, когда нам необходимо будет обойти CSRF защиту (совершайте это действие крайне осознанно!), указав необходимые url в потомке оригинального core-вского VerifyCsrfToken.php класса App\Http\Middleware\VerifyCsrfToken следующим образом:
<?php namespace App\Http\Middleware; use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier; class VerifyCsrfToken extends BaseVerifier { /** * The URIs that should be excluded from CSRF verification. * * @var array */ protected $except = [ //url запросов, для которых не нужно проверять CSRF токен ]; }
Вот и всё, что нужно для исключения url от проверки на CSRF безопасность.
И на этом данная статья подходит к концу. В следующей я покажу вам, как реализовывать в Laravel AJAX запросы с применением сегодняшних знаний.
Пишите в комментариях ваши отзывы и вступайте в наши социальные сообщества, где вы можете получить ответ на все интересующие вас оффтопные темы.
На этом всё. Пока 🙂
P.S.: если вам нужен сайт либо необходимо внести правки на существующий, но для этого нет времени и желания, могу предложить свои услуги.
Более 5 лет опыта профессиональной разработки сайтов. Работа с PHP, OpenCart, WordPress, Laravel, Yii, MySQL, PostgreSQL, JavaScript, React, Angular и другими технологиями web-разработки.
Опыт разработки проектов различного уровня: лендинги, корпоративные сайты, Интернет-магазины, CRM, порталы. В том числе поддержка и разработка HighLoad проектов. Присылайте ваши заявки на email cccpblogcom@gmail.com.
И с друзьями не забудьте поделиться 😉
Спасибо, очень полезная информация, все изложено очень доходчиво!
Только добавьте, плиз, частичку «не» перед словом «рекомендуется» в конце этого абзаца:
«…При отправке же GET запроса для успешной его обработки сервером CSRF token указывать не нужно. Но, как известно, данный тип запроса и так считается небезопасным благодаря указанию параметров в URL запроса, которые можно увидеть в браузере. Поэтому пользоваться им для передачи конфиденциальных и важных данных не то, что рекомендуется, а запрещается.»
Спасибо Вам за отзыв и сообщение об ошибке 🙂
Спасибо! Хорошая статья, информативная уж точно. Странно, что комментов нет)
И Вам спасибо за отзыв 🙂
Спасибо, полезная статья.
А как этот токен обновлять, допустим, каждые 15 мин, чтобы он не устаревал? А то пользователи бывают оставляют надолго открытой вкладку и через несколько часов только продолжают пользоваться сайтом, а за это время токен устаревает и ajax запросы не выполняются. Прям беда у меня с этим.
Или единственный выход ставить время жизни SESSION_LIFETIME на целый день?
День добрый. Спасибо за отзыв 🙂
Вы уверены, что проблема в устаревании токена? Какую ошибку выдаёт Laravel? И, возможно, в логах больше информации содержится?
Добрый день, я совсем чайник в этих делах, подскажите, что означает в коде сайта мета-теги:
Простите за мою несусветную тупость, но это что значит — на сайте защита от csrf или это сайт мошенников?
Да, всё в порядке. Это стандартная CSRF-защита. Сами разработчики Laravel предлагают такую имплементацию — https://laravel.com/docs/5.8/csrf#csrf-x-csrf-token