Что такое csrf token
CSRF-токен
В этой статье рассмотрим, что такое Сross-Site Request Forgery и как включить в Spring Boot приложение CSRF-токен — защиту от этого мошенничества. Назвать этот токен можно было бы «анти-CSRF-токен».
Same Origin Policy и CSRF (cross-site request forgery)
Обычно запросы, сделанные в браузере с одного домена на другой, не проходят, поскольку браузер придерживается Same Origin Policy. Это политика безопасности, защищающая одни сайты от других.
Например, пусть пользователь случайно заходит на домен evil.com (мошеннический сайт) и щелкает там яркую кнопку, которая выполняет либо PUT-запрос, либо DELETE-запрос на bank.com. И так случайно получилось, что этот пользователь зарегистрирован на bank.com и в браузере хранятся куки к нему. Благодаря политике безопасности браузера, ничего плохого не случится. Потому что браузер сначала вышлет так называемый «preflight», то есть предварительный OPTIONS-запрос на bank.com с заголовком
и затем отправит за ним настоящий PUT/DELETE-запрос, но только в том случае, если в ответе пришло разрешение на отправку запросов от evil.com. В противном случае в метод PUT/DELETE банковского сайта мы даже не попадем.
Но есть запросы, которые отправляются сразу, без предварительного запроса, так называемые simple requests. Это GET, HEAD, POST с определенным Content-Type. В частности, POST-запросы с формы. Такой запрос сразу идет в контроллер. А учитывая, что куки браузер отправляет автоматически, запрос попадет в защищенный контроллер и выполнит действие на банковском сайте. Хотя ответ получить и распарсить нельзя, действие будет выполнено.
Ниже рассмотрим, как это происходит. В примере одно приложение на одном домене делает POST-запрос на другой домен, где работает второе приложение. Рассмотрим, как защититься от таких запросов с помощью CSRF-токена.
Пример жульничества
Создадим два приложения на Spring Boot:
Поскольку в примере мы будем делать запрос на «другой домен», пропишем для localhost:8080 новое имя в файле hosts:
А именно, добавим в файл hosts строку:
Теперь к приложению-мишени будем обращаться по адресу http://bank-server:8080/..вместо http://localhost:8080..
Приложение-мишень
Итак, пусть в нашем приложении есть контроллер и форма, с которой что-то добавляется:
Добавление
Форма на Thymeleaf выглядит так:
При этом адрес http://bank-server:8080/add защищен, доступ к нему возможен только благодаря куки JSESSIONID после входа с помощью формы логина.
В приложении задан единственный in-memory пользователь с именем user и паролем user:
Выше прописано, что все запросы доступны только аутентифицированным пользователям (в том числе наша форма — ее получение по адресу /add методом GET и отправка методом POST). Форма находится в шаблоне add.html
Проверку CSRF-токена мы отключили выше отдельной строкой:
Это сделано для того, чтобы продемонстрировать атаку с помощью второго приложения ниже. А затем включить CSRF-токен обратно. (По умолчанию он и так включен, просто нужно не забывать добавлять его и на форму, что будет в показано самом конце).
Мошенническое приложение
Второе приложение совсем простое, оно состоит из одного view с кнопкой атаки POST (полный код тут):
Сайт жуликов
Кнопка отправляет форму со скрытыми полями: и text=«document». Если в браузере мы залогинены в приложении http://bank-server:8080 и откроем мошеннический сайт localhost:8081 с вышеприведенной кнопкой и щелкнем ее, то сохраненный куки JSESSIONID отправляется тоже, и мы попадаем в защищенный метод add() контроллера DocumentController банковского сайта. Документ добавляется.
Защита с помощью CSRF-токена
Чтобы защититься от таких запросов, нужно включить CSRF-токен, то есть убрать строку отключения, которую мы добавили выше:
Теперь когда пользователь логинится на сайт http://bank-server:8080, ему выделяется специальный CSRF-токен. Он хранится в сессии и должен отправляться как скрытое поле со всех форм (также он должен прилагаться в XMLHttpRequest-запросах PUT, DELETE, POST — это для JavaScript). Выше мы показали только одну атаку, но при определенных настройках Spring MVC в определенных браузерах возможны и более сложные атаки. И хотя браузеры становятся все более безопасными, как и сам Spring MVC, все равно Spring Security имеет вот такое требование и для PUT, и для DELETE-запросов, хотя для них существуют «preflight» запросы от браузера. Но эти настройки CSRF-токена можно и поменять — убрать некоторые методы или некоторые url — сделать так, чтобы для них не требовался токен.
Итак, токен в POST-запросах сейчас требуется. Если оставить все как есть, то наше собственное банковское приложение не заработает — при попытке отправить форму получим 403. Надо сделать так, чтобы выданный CSRF-токен отправлялся. Для этого добавим его как скрытое поле на форму в наш Thymeleaf-шаблон:
Все, теперь и наше приложение работает, и с чужого сайта форму отправить нельзя, потому что мошенники не знают CSRF-токен. В отличие от JSESSIONID, он не хранится в куки браузера и не отправляется автоматически при запросах на банковский сайт.
Итоги
Исходный код обоих приложений есть на GitHub.
CSRF-защита
Введение
Laravel автоматически генерирует CSRF-«токен» для каждой активной пользовательской сессии в приложении. Этот токен используется для проверки того, что именно авторизованный пользователь делает запрос в приложение.
При определении каждой HTML-формы вы должны включать в неё скрытое поле CSRF-токена, чтобы посредник CSRF-защиты мог проверить запрос. Вы можете использовать вспомогательную функцию PHP csrf_field () для генерирования поля токена:
Исключение URI из CSRF-защиты
Иногда бывает необходимо исключить набор URI из-под CSRF-защиты. Например, если вы используете Stripe для обработки платежей и применяете их систему веб-хуков (hook), то вам надо исключить маршрут вашего обработчика веб-хуков Stripe из-под CSRF-защиты, так как Stripe не будет знать, какой CSRF-токен надо послать в ваш маршрут.
X-CSRF-TOKEN
После создания тега meta вы можете указать библиотеке, такой как jQuery, автоматически добавлять токен в заголовки всех запросов. Это обеспечивает простую, удобную CSRF-защиту для ваших приложений на базе AJAX:
X-XSRF-TOKEN
Комментарии (7)
не поняла немного. что означает : «Посредник VerifyCsrfToken, входящий в группу посредников web, автоматически проверяет совпадение токена в данных запроса с токеном, хранящимся в сессии»
понятие «токен в зданных запроса» откуда он берется?
csrf_field() во вьюхе рендерится как
Встроил в форму предложенную функцию. Все равно форма не отправляется
Laravel CSRF: обзор понятий и реализаций защиты
Здравствуйте, уважаемые посетители 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 следующим образом:
Кстати, данный хэлпер появился сравнительно недавно — в Laravel 5.1. Так что, если вы используете более раннюю версию, то можете воспользоваться следующей конструкцией вместо предложенной:
Также разработчики Laravel рекомендуют для дополнительной защиты AJAX запросов добавлять CSRF токен ко всем запросам на уровне JavaScript путём указания токена в значении метатэга, размещённого в секции head HTML-документа:
Считывание данного значения в JS производится следующей JQuery-конструкцией:
При отправке же 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 следующим образом:
Вот и всё, что нужно для исключения url от проверки на CSRF безопасность.
И на этом данная статья подходит к концу. В следующей я покажу вам, как реализовывать в Laravel AJAX запросы с применением сегодняшних знаний.
Пишите в комментариях ваши отзывы и вступайте в наши социальные сообщества, где вы можете получить ответ на все интересующие вас оффтопные темы.
P.S.: если вам нужен сайт либо необходимо внести правки на существующий, но для этого нет времени и желания, могу предложить свои услуги.
Более 5 лет опыта профессиональной разработки сайтов. Работа с PHP, OpenCart, WordPress, Laravel, Yii, MySQL, PostgreSQL, JavaScript, React, Angular и другими технологиями web-разработки.
Опыт разработки проектов различного уровня: лендинги, корпоративные сайты, Интернет-магазины, CRM, порталы. В том числе поддержка и разработка HighLoad проектов. Присылайте ваши заявки на email cccpblogcom@gmail.com.
И с друзьями не забудьте поделиться 😉
What is a CSRF token? What is its importance and how does it work?
I am writing an application (Django, it so happens) and I just want an idea of what actually a «CSRF token» is and how it protects the data.
Is the post data not safe if you do not use CSRF tokens?
5 Answers 5
Cross-Site Request Forgery (CSRF) in simple words
This is the world without CSRF tokens.
Now for the better one with CSRF tokens:
Result: You keep your 10000 monetary units. I suggest you donate some of that to Wikipedia.
(Your mileage may vary.)
It would be worthy to note that script from www.cute-cat-pictures.org normally does not have access to your anti-CSRF token from www.mybank.com because of HTTP access control. This note is important for some people who unreasonably send a header Access-Control-Allow-Origin: * for every website response without knowing what it is for, just because they can’t use the API from another website.
Yes, the post data is safe. But the origin of that data is not. This way somebody can trick user with JS into logging in to your site, while browsing attacker’s web page.
In order to prevent that, django will send a random key both in cookie, and form data. Then, when users POSTs, it will check if two keys are identical. In case where user is tricked, 3rd party website cannot get your site’s cookies, thus causing auth error.
The site generates a unique token when it makes the form page. This token is required to post/get data back to the server.
Imagine you had a website like a simplified Twitter, hosted on a.com. Signed in users can enter some text (a tweet) into a form that’s being sent to the server as a POST request and published when they hit the submit button. On the server the user is identified by a cookie containing their unique session ID, so your server knows who posted the Tweet.
The form could be as simple as that:
Now imagine, a bad guy copies and pastes this form to his malicious website, let’s say b.com. The form would still work. As long as a user is signed in to your Twitter (i.e. they’ve got a valid session cookie for a.com), the POST request would be sent to http://a.com/tweet and processed as usual when the user clicks the submit button.
So far this is not a big issue as long as the user is made aware about what the form exactly does, but what if our bad guy tweaks the form like this:
Now, if one of your users ends up on the bad guy’s website and hits the “Click to win!” button, the form is submitted to your website, the user is correctly identified by the session ID in the cookie and the hidden Tweet gets published.
If our bad guy was even worse, he would make the innocent user submit this form as soon they open his web page using JavaScript, maybe even completely hidden away in an invisible iframe. This basically is cross-site request forgery.
A form can easily be submitted from everywhere to everywhere. Generally that’s a common feature, but there are many more cases where it’s important to only allow a form being submitted from the domain where it belongs to.
How do I make sure a form can only be submitted from my own website? This is where the CSRF token comes in. A CSRF token is a random, hard-to-guess string. On a page with a form you want to protect, the server would generate a random string, the CSRF token, add it to the form as a hidden field and also remember it somehow, either by storing it in the session or by setting a cookie containing the value. Now the form would look like this:
When the user submits the form, the server simply has to compare the value of the posted field csrf-token (the name doesn’t matter) with the CSRF token remembered by the server. If both strings are equal, the server may continue to process the form. Otherwise the server should immediately stop processing the form and respond with an error.
Why does this work? There are several reasons why the bad guy from our example above is unable to obtain the CSRF token:
Copying the static source code from our page to a different website would be useless, because the value of the hidden field changes with each user. Without the bad guy’s website knowing the current user’s CSRF token your server would always reject the POST request.
Because the bad guy’s malicious page is loaded by your user’s browser from a different domain (b.com instead of a.com), the bad guy has no chance to code a JavaScript, that loads the content and therefore our user’s current CSRF token from your website. That is because web browsers don’t allow cross-domain AJAX requests by default.
The bad guy is also unable to access the cookie set by your server, because the domains wouldn’t match.
When should I protect against cross-site request forgery? If you can ensure that you don’t mix up GET, POST and other request methods as described above, a good start would be to protect all POST requests by default.
You don’t have to protect PUT and DELETE requests, because as explained above, a standard HTML form cannot be submitted by a browser using those methods.
This means, often you do not even have to add a CSRF token to AJAX requests, even if they are POST requests, but you will have to make sure that you only bypass the CSRF check in your web application if the POST request is actually an AJAX request. You can do that by looking for the presence of a header like X-Requested-With, which AJAX requests usually include. You could also set another custom header and check for its presence on the server side. That’s safe, because a browser would not add custom headers to a regular HTML form submission (see above), so no chance for Mr Bad Guy to simulate this behaviour with a form.
If you’re in doubt about AJAX requests, because for some reason you cannot check for a header like X-Requested-With, simply pass the generated CSRF token to your JavaScript and add the token to the AJAX request. There are several ways of doing this; either add it to the payload just like a regular HTML form would, or add a custom header to the AJAX request. As long as your server knows where to look for it in an incoming request and is able to compare it to the original value it remembers from the session or cookie, you’re sorted.
Laravel 8 · Предотвращение атак CSRF
Введение
Межсайтовая подделка запроса – это разновидность вредоносного эксплойта, при котором неавторизованные команды выполняются от имени аутентифицированного пользователя. К счастью, Laravel позволяет легко защитить ваше приложение от Межсайтовой подделки запроса (Сross Site Request Forgery – CSRF).
Объяснение уязвимости
Без защиты от CSRF вредоносный веб-сайт может создать HTML-форму, которая указывает на маршрут вашего приложения /user/email и отправляет собственный адрес электронной почты злоумышленника:
Если вредоносный веб-сайт автоматически отправляет форму при загрузке страницы, злоумышленнику нужно только подтолкнуть ничего не подозревающего пользователя вашего приложения посетить свой веб-сайт, и его адрес электронной почты будет изменен в вашем приложении.
Предотвращение запросов от CSRF
Laravel автоматически генерирует «токен» CSRF для каждой активной пользовательской сессии, управляемой приложением. Этот токен используется для проверки того, что аутентифицированный пользователь действительно является лицом, выполняющим запросы к приложению. Поскольку этот токен хранится в сессии пользователя и изменяется каждый раз при повторном создании сессии, вредоносное приложение не может получить к нему доступ.
К CSRF-токену текущей сессии можно получить доступ через сессию запроса или с помощью глобального помощника csrf_token :
Каждый раз, когда вы создаете HTML-форму в своем приложении, вы должны включать в форму скрытое поле _token CSRF, чтобы посредник CSRF мог проверить запрос. Для удобства вы можете использовать директиву Blade @csrf для создания скрытого поля ввода, содержащего токен:
CSRF-токены и SPA-приложения
Если вы создаете SPA, который использует Laravel в качестве серверной части API, вам следует обратиться к документации Laravel Sanctum для получения информации об аутентификации с помощью вашего API и защите от уязвимостей CSRF.
Исключение URI из защиты от CSRF
По желанию можно исключить набор URI из защиты от CSRF. Например, если вы используете Stripe для обработки платежей и используете их систему веб-хуков, вам нужно будет исключить маршрут обработчика веб-хуков Stripe из защиты от CSRF, поскольку Stripe не будет знать, какой токен CSRF отправить вашим маршрутам.
Для удобства посредник CSRF автоматически отключается для всех маршрутов при выполнение тестов.
Токен X-CSRF
Затем, вы можете указать библиотеке, такой как jQuery, автоматически добавлять токен во все заголовки запросов. Это обеспечивает простую и удобную защиту от CSRF для ваших приложений с использованием устаревшей технологии JavaScript на основе AJAX:
Токен X-XSRF
Этот файл Cookies, в первую очередь, отправляется для удобства разработчика, поскольку некоторые фреймворки и библиотеки JavaScript, такие как Angular и Axios, автоматически помещают его значение в заголовок X-XSRF-TOKEN в запросах с одним и тем же источником.