Если вы столкнулись с ошибкой «истек CSRF-токен» — читайте нашу статью. Из неё вы узнаете, как работает CSRF-token защита, и что делать, если CSRF токен истек.
Что такое CSRF
CSRF (англ. cross-site request forgery) — это межсайтовая подделка запроса. Это атака, которой может подвергаться любой веб-ресурс или веб-приложение. В первую очередь это касается сайтов, которые используют cookies, сертификаты авторизации и браузерную аутентификацию. В результате атаки страдают клиенты и репутация ресурса.
Вредоносный скрипт прячется в коде сайта или обычной ссылке. С помощью него мошенник получает доступ к конфиденциальной информации: платежным реквизитам, логину и паролю, личной переписке. После того как данные “в кармане”, хакер может изменить пароль, указать свой номер телефона или email, перевести деньги на свой счёт и многое другое.
Как работает CSRF-атака
Злоумышленник может использовать фишинговую ссылку — это наиболее распространенный способ обмана. В этом случае атака работает по следующей схеме:
- Злоумышленник создаёт поддельную страницу, очень похожую на оригинальную, и встраивает её в сайт. В коде ссылка может выглядеть так: <a href=“вредоносная ссылка”>Unsubscribe here</a>.
- Пользователь переходит с одной страницы сайта на другую (например, на страницу оплаты) и вместо реальной страницы попадает на поддельную.
- Пользователь совершает действие на странице, например, оплачивает товар или вводит данные авторизации.
- Информация или денежные средства вместо оригинального сервера уходят на сервер мошенника.
CSRF-атаки случаются из-за того, что без специальных настроек сервер не может с точностью в 100% определить, кто именно выполняет действия со стороны пользователя. Он не может проверить, действительно ли на кнопку “оплатить” нажал тот пользователь, который изначально открыл страницу с оплатой. Хакеры активно используют этот люфт в безопасности HTTP-запросов и применяют вредоносные скрипты. Однако от атаки можно защититься с помощью CSRF-токенов.
Что такое CSRF-token и как он работает
В общем понимании токен — это механизм, который позволяет идентифицировать пользователя или конкретную сессию для безопасного обмена информацией и доступа к информационным ресурсам. Токены помогают проверить личность пользователя (например, клиента, который онлайн получает доступ к банковскому счёту). Их используют как вместо пароля, так и вместе с ним. Токен — это в каком-то смысле электронный ключ.
CSRF-token — это максимально простой и результативный способ защиты сайта от CSRF-мошенников. Он работает так: сервер создаёт случайный ключ (он же токен) и отправляет его браузеру клиента. Когда браузер запрашивает у сервера информацию, сервер, прежде чем дать ответ, требует показать ключ и проверяет его достоверность. Если токен совпадает, сессия продолжается, а если нет — прерывается. Токен действителен только одну сессию — с новой сессией он обновляется.
Чтобы получить ответ от сервера, используются разные методы запроса. Условно они делятся на две категории: те, которые не изменяют состояние сервера (GET, TRACE, HEAD), и те, которые изменяют (PUT, PATCH, POST и DELETE). Последние имеют большую CSRF-уязвимость и поэтому должны быть защищены в первую очередь.
При создании и использовании токена должны соблюдаться следующие условия:
-
нахождение в скрытом параметре;
-
генерация с помощью генератора псевдослучайных чисел;
-
ограниченное время жизни (одна сессия);
-
уникальность для каждой транзакции;
-
устойчивый к подбору размер (в битах);
-
невозможно переиспользовать.
Типы токенов
Существует три основных типа токенов по способу генерации:
- Synchronizer Tokens или Anti-CSRF (токены синхронизации). В этом случае инициатором ключа выступает сервер — на нём хранится исходная шифровка. Когда браузер обращается к серверу и предъявляет ему ключ, сервер сравнивает его с исходником и в зависимости от результата продолжает или прерывает сессию.
- Double Submit Cookie (двойная отправка куки). При этом способе токен нигде не хранится. Когда браузер обращается к серверу впервые за сессию, сервер генерирует и передаёт ему ключ в двух формах: через куки и в одном из параметров ответа. При следующих обращениях браузера сервер дважды проверяет правильность ключа — в параметрах и в куках.
- Encrypted Token (зашифрованный токен). Этот способ предполагает, что ключом шифруется какая-то часть информации о клиенте, которая содержится в браузере. При первом запросе браузера сервер получает информацию о пользователе, зашифровывает её и передаёт браузеру токен. При следующем взаимодействии сервер расшифровывает токен и сверяет информацию.
Помимо токенов, для защиты используется флаг Same-Site (большинство браузеров его поддерживает). Он работает напрямую для cookies и позволяет помечать куки конкретного домена. Сервер проверяет, содержатся ли нужные пометки в куках страницы, с которых происходит оплата или вносятся изменения. Если пометок нет — сессия прекращается.
Также в качестве меры защиты на страницах сайта настраивают форму с капчей. Это особенно актуально для страниц смены пароля или совершения денежных транзакций.
«Истек срок действия токена» или «CSRF-значение недопустимо»: что это значит и что делать
Даже при авторизации на сайтах, для которых настроена защита от атак, можно встретить следующие варианты сообщения об ошибке: «Недопустимое CSRF-значение»/«CSRF-токены не совпадают» или «Token expired» (в переводе — срок действия токена истек). Сообщение может отображаться как на английском, так и на русском. Пример ошибки при авторизации на сайте REG.RU:
Обычно ошибка возникает по двум основным причинам:
-
сервер некорректно сгенерировал токен;
-
срок токена истек — пользователь долго не совершал никаких действий на странице.
В обоих случаях исправить проблему поможет перезагрузка страницы — вы запустите новую сессию, а значит, сервер и браузер договорятся о новом рабочем токене. Для этого нажмите на значок обновления страницы:
Иногда ошибка возникает из-за расширений защиты конфиденциальности или плагинов блокировки рекламы (например, Ghostery, UBlock Origin, Blur), которые настроены у пользователя. В этом случае можно отключить расширение. Также можно добавить сайт, на котором появилось сообщение, в список доверенных сайтов.
На примере сайта reg.ru покажем, что для этого нужно:
в Google Chrome
- Откройте настройки Chrome:
- В списке слева выберите Конфиденциальность и безопасность, а затем Файлы cookie и другие данные сайтов.
- Внизу страницы откройте Сайты, которые всегда могут использовать файлы cookie и кликните Добавить.
- Введите «[*.]www.reg.ru» и нажмите Добавить.
- Нажмите Все файлы cookie и данные сайта и удалите все записи, которые связаны с сайтом reg.ru.
- Перезагрузите браузер и выполните операцию повторно.
в Яндекс.Браузер
-
Откройте настройки браузера Яндекс:
- Перейдите на Сайты — Расширенные.
- Кликните Настройки… для первого параметра в списке. Затем на вкладке «Разрешена» введите www.reg.ru и кликните Добавить.
- Добавьте адрес сайта для всех параметров списка по аналогии.
в Safari
- Откройте настройки Safari комбинацией Cmd + , (⌘,).
- Перейдите на вкладку Конфиденциальность и проверьте, что в пункте «Файлы cookie и данные веб-сайтов» не выбрано «Блокировать все файлы cookie». Если это так, снимите настройки.
- Кликните Управление данными веб-сайтов и удалите все записи, которые относятся к www.reg.ru.
- Перезагрузите браузер и выполните операцию повторно.
В некоторых случаях сгенерировать верный токен мешают локальные настройки куки в браузере. Чтобы сессия прошла успешно, достаточно вернуть дефолтные настройки.
Заключение
Успешная атака CSRF позволяет хакеру действовать на сайте от имени другого зарегистрированного посетителя. Чтобы мошенник не добрался до конфиденциальных данных, для сайта нужно настроить один из типов CSRF-токенов. Токены позволяют серверу и браузеру безопасно обмениваться информацией в течение сессии. Однако даже на безопасных сайтах можно столкнуться с ошибкой «токен CSRF истек». В этом нет ничего страшного. Чтобы возобновить подключение, достаточно обновить страницу браузера.
Эта статья научит вас, как исправить ошибку «CSRF token истёк», которую вы можете получить при посещении сайта, и больше расскажет о CSRF-атаках в целом.
Что такое CSRF
CSRF (от англ. cross-site request forgery) или межсайтовая подделка запроса – это форма атаки на любой сайт или веб-приложение. С помощью подделки запросов мошенник обманом заставляет пользователя выполнять нежелательные действия на, казалось бы, проверенной платформе.
Как работает CSRF-атака
Межсайтовая подделка запроса будет работать только в том случае, если потенциальная жертва авторизована. Благодаря этому злоумышленник может обойти процесс аутентификации, чтобы войти в веб-приложение.
Выполнение CSRF-атаки состоит из двух основных частей.
- Злоумышленник заставляет жертву щёлкнуть на ссылку или загрузить веб-страницу. Он намеренно заманивает пользователя перейти по ссылке, используя методы социальной инженерии.
- Отправляет «поддельный» или выдуманный запрос в браузер жертвы. Вредоносная ссылка отправит запрос в веб-приложение, однако будет включать в себя значения, которые выгодны злоумышленнику.
Этот запрос определяется как незаконный, поскольку жертва не знает, что он отправляется. Но для веб-сервера это выглядит так, как будто пользователь отправил его, потому что он включает в себя файлы cookie, которые необходимы веб-серверу для проверки личности жертвы.
Примеры CSRF-атаки
Для того, чтобы получить ответ от сервера, в протоколе применяют различные методы HTTP-запросов. Наиболее часто используемые – это GET, POST, PUT, PATCH и DELETE.
Они используются и для создания межсайтовой подделки запроса.
Пример GET CSRF-атаки:
В следующем примере показано, как выглядит типичный GET-запрос для банковского перевода в размере 1000 долларов.
GET https://randombank.ru/transfer.do?account=RandPerson&amount=$1000 HTTP/1.1
Злоумышленник может изменить ссылку таким образом, что она приведёт к переводу 1000 долларов на его личный счет. Вредоносный запрос будет выглядеть так:
GET https://randombank.ru/transfer.do?account=SomeAttacker&amount=$1000 HTTP/1.1
Если задействованное приложение ожидает GET-запрос, злоумышленник может разместить на своем веб-сайте тег, который вместо ссылки на изображение отправит поддельный запрос.
<img src=»вредоносная ссылка» />
Пример POST CSRF-атаки:
<h1>Вы выиграли приз!</h1>
<form action=” www.shopshop.ru/api/account» method=”post”>
<input type=”hidden” name=”Transaction” value=”withdraw” />
<input type=”hidden” name=”Amount” value=”1000000″ />
<input type=”submit” value=”Click Me”/>
</form>
- Пользователь входит на сайт www.shopshop.ru с помощью своих логина и пароля.
- Сервер авторизует пользователя, а ответ от сервера включает файл cookie аутентификации.
- Не выходя из системы, пользователь посещает вредоносную веб-страницу. Этот сайт содержит HTML-форму, указанную выше.
- Пользователь нажимает кнопку отправки. Браузер отправляет файл cookie аутентификации вместе с запросом.
- Сервер обрабатывает запрос, учитывая, что пользователь уже находится в аккаунте, поэтому злоумышленник получает доступ ко всему, что разрешено делать авторизованному пользователю.
Что такое токен CSRF
Токен CSRF – это уникальное и непредсказуемое значение, которое сервер генерирует для защиты уязвимых перед CSRF ресурсов.
После выполнения запроса приложение на стороне сервера сравнивает два маркера, найденные в сеансе пользователя и в самом запросе. Если токен отсутствует или не соответствует значению в сеансе пользователя, запрос отклоняется, сеанс пользователя завершается, а событие регистрируется как потенциальная атака CSRF. Так злоумышленнику практически невозможно создать полный действительный запрос, чтобы заманить жертву.
Таким образом, можно выделить основные признаки токена CSRF:
- уникальность при каждом запросе;
- непродолжительное время жизни,
- непредсказуемость и устойчивый к подбору.
Ошибки CSRF: почему возникают и как их исправить?
Сообщения «Неверный токен CSRF» или «Срок действия токена CSRF истёк» означает, что ваш браузер не смог создать безопасный файл cookie или не смог получить доступ к этому файлу cookie для авторизации вашего входа в систему.
Это может быть вызвано плагинами, расширениями, блокирующими рекламу, а также самим браузером, если ему не разрешено устанавливать файлы cookie.
Есть несколько причин, по которым вы можете получать сообщения об ошибках:
- срок действия старого токена действительно истёк, так как прошло более 24 часов;
- уже был отправлен новый токен, в связи с чем старый стал недействительным;
- плагины или расширения блокируют запросы;
- браузеру не разрешено устанавливать файлы cookie.
Иногда нужно лишь обновить свою страницу или вновь войти в систему, и всё будет готово для продолжения вашей работы. Если это не помогает, есть способы исправить это в разных браузерах.
Как исправить ошибки CSRF в браузерах
Google Chrome
- В правом верхнем углу экрана щёлкните на значок с тремя точками и в меню выберите Настройки.
- В панели слева откройте вкладку Конфиденциальность и безопасность.
- Выберите Файлы cookie и другие данные сайтов.
- Пролистайте до раздела «Специальные настройки» и щёлкните Добавить рядом с графой «Сайты, которые всегда могут использовать файлы cookie».
- Чтобы включить сайт в этот список, введите название нужного сайта по форме [*.]ваш.сайт и затем нажмите Добавить.
- В разделе Посмотреть все разрешения и данный сайтов найдите nic.ru и удалите все записи, связанные с сайтом.
- Перезагрузите браузер и вновь войдите на сайт.
Microsoft Edge
- Откройте меню в правом верхнем углу экрана и щёлкните Настройки.
- В параметрах перейдите в раздел Файлы cookie и разрешения сайтов.
- Нажмите «Управляйте файлами cookie и данными сайта…».
- Нажмите Добавить рядом со словом «Разрешить», чтобы внести сайт в список и позволить браузеру сохранять с него файлы cookie.
- Введите [*.]ваш.сайт и подтвердите своё решение, нажав Добавить.
- Перейдите во вкладку Посмотреть все файлы cookie и данные сайта и удалите всё, связанное с выбранным сайтом.
- Перезагрузите браузер.
Яндекс.Браузер
- Откройте меню, щёлкнув на значок с тремя линиями в верхнем углу экрана, и перейдите в Настройки.
- В левой панели выберите вкладку Сайты и найдите в ней опцию Расширенные настройки сайтов.
- Пролистайте вниз до раздела Cookie-файлы. Откройте Настройки сайтов под ним.
- Нажмите Добавить.
- Введите [*.]ваш.сайт и подтвердите своё решение, нажав Добавить.
- Вернитесь на предыдущую страницу и перейдите в Cookie-файлы и данные сайтов. Кнопка находится рядом с Настройками сайтов.
- Удалите все данные о сайте.
- Перезагрузите браузер.
Opera
- В левой панели экрана щёлкните на значок Настроек.
- Выберите раздел Безопасность, а затем нажмите Файлы cookie и прочие данные сайтов.
- В разделе «Настраиваемое поведение» у параметра Сайты, которые всегда могут использовать файлы cookie щёлкните Добавить.
- Введите [*.]ваш.сайт и подтвердите своё решение, нажав Добавить.
- Чуть выше выберите опцию Все файлы cookie и данные сайта и удалите все данные о сайте.
- Перезагрузите ваш браузер и вновь откройте сайт.
Safari
- Откройте Настройки Safari в верхней части экрана или с помощью сочетания клавиш Cmd + ,.
- Перейдите на вкладку Конфиденциальность и убедитесь, что для параметра «Файлы cookie и данные веб-сайтов» не установлено значение «Блокировать все файлы cookie».
- Затем нажмите Управлять данными веб-сайта…, найдите [ваш.сайт] и удалите все записи, связанные с сайтом.
- Перезагрузите Safari и проверьте сайт на наличие ошибки.
Заключение
Файлы cookie изначально уязвимы перед CSRF-атаками, поскольку они автоматически отправляются с каждым запросом. Это позволяет злоумышленникам легко создавать вредоносные запросы, которые приводят к межсайтовым подделкам запросов. Хотя извлечение конфиденциальной информации не является основной целью атаки CSRF, это может оказать неблагоприятное воздействие на используемое приложение.
В этой статье вы узнали больше о явлении CSRF-атак, токенах, а также о том, что именно защищает вас от уловок современных злоумышленников и что делать, если появляются ошибки «Токен-CSRF истёк» или «Неверный токен CSRF».
You should always check if CSRF protection is working. With Flask this is not obvious.
https://unsplash.com/@christineashleydonaldson
I never really checked if CSRF protection was working in my Flask application, this website. Is it enabled by default? From the Flask_WTF extension documentation:
Any view using FlaskForm to process the request is already getting CSRF protection.
And from the text of Miguel Grinberg’s post ‘Cookie Security for Flask Applications’:
If you are handling your web forms with the Flask-WTF extension, you are already protected against CSRF on your forms by default.
It should be enabled, let’s check if this is true. On the page with the form I added:
<script>
$(document).ready(function(){
$('#csrf_token').val('ABC');
});
</script>
Then I refreshed the form. In the debugger I verified that the csrf_token changed to ‘ABC’. Clicking the submit button should give a CSRF error, well I expected a CSRF exception. For me, no exception, which means no CSRF protection. How is this possible? Did it have to do with the fact that I am using DispatcherMiddleWare or is something else wrong?
Yes, something was wrong. Me! And it was caused by TL;DR. There was in fact a CSRF error but not a CSRF exception. I have many forms, mostly in the admin section, and they have been working with CSRF protection all the time without any CSRF error. What is going on?
My testapp:
def create_app():
fname = 'create_app'
print(fname + '()')
app = Flask(__name__)
app.config['SECRET_KEY'] = 'some-secret-key'
class MyCSRFForm(FlaskForm):
submit = SubmitField(_l('Send'))
@app.route('/csrf_form', methods=['GET', 'POST'])
def csrf_form():
fname = 'csrf_form'
print(fname + '()')
form = MyCSRFForm()
if form.validate_on_submit():
print(fname + ': no validate_on_submit errors, processing form')
print(fname + ': form.errors = '.format(form.errors))
for field, errors in form.errors.items():
print(fname + ': form.errors, field = {}, errors = {}'.format(field, errors))
return render_template('csrf_form.html', form=form)
and the csrf_form.html template:
<html>
<head></head>
<body>
<form method="post">
{{ form.csrf_token }}
<p>{{ form.submit() }}</p>
</form>
<script>
elem = document.getElementById('csrf_token');
elem.setAttribute('value', 'ABC');
</script>
</body>
</html>
The result:
csrf_form: form.errors, field = csrf_token, errors = ['The CSRF token is invalid.']
There is more information about this in ‘Inconsistency with raising CSRFError #381’, see the links below. It appears that you can use Flask-WTF CSRF protection in two ways:
- as a CSRF error message during form validation
- as a CSRF exception
Both have advantage and disadvantages. I decided to go for the CSRF exception because there is no way you can forget this by accident. To generate CSRF exceptions, I added the suggested code in my __init__.py:
from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect()
...
def create_app(project_config):
...
# csrf protection
csrf.init_app(app)
Now when I submit the form I get a CSRF exception:
Bad Request
The CSRF token is invalid.
I changed the jQuery and removed the field name csrf_token:
<script>
$(document).ready(function(){
$('#csrf_token').attr({name: 'nothing'});
});
</script>
Refresh and submit, the CSRF exception is:
Bad Request
The CSRF token is missing.
And as a final test we remove the script and set the CSRF token expire time to 5 seconds in create_app():
app.config['WTF_CSRF_TIME_LIMIT'] = 5
The CSRF exception is:
Bad Request
The CSRF token has expired.
Summary of these tests:
- The http error code is 400 Bad Request
- It appeared CSRF protection was working by default and can be implemented in two ways, the documentation is not very clear on this
- I wanted CSRF exceptions and added the suggested extra code
- It was very easy to generate CSRF exceptions for the conditions:
— The CSRF token is invalid
— The CSRF token is missing
— The CSRF token has expired
Multiple forms and the CSRF token
A blog post page on this site has two types of comment forms: the comment form and the comment reply form, see also a previous post.
When looking at the page with the comment form and comment reply form I noticed that both had the CSRF token value after a page load.
comment form:
<input id="comment_form-csrf_token" name="comment_form-csrf_token" type="hidden" value="IjM4N2Y3NmEzMGFkNGMwOTY2MzM5OGI0NTVjOGQwNDI3MjY3MmQxOGIi.Xk6ReA.dFbMlM0bClY6BvAVjxZ0GfiFBM4">
comment reply form:
<input id="comment_reply_form-csrf_token" name="comment_reply_form-csrf_token" type="hidden" value="IjM4N2Y3NmEzMGFkNGMwOTY2MzM5OGI0NTVjOGQwNDI3MjY3MmQxOGIi.Xk6ReA.dFbMlM0bClY6BvAVjxZ0GfiFBM4">
or:
comment_form-csrf_token: IjM4N2Y3NmEzMGFkNGMwOTY2MzM5OGI0NTVjOGQwNDI3MjY3MmQxOGIi.Xk6ReA.dFbMlM0bClY6BvAVjxZ0GfiFBM4
comment_reply_form-csrf_token: IjM4N2Y3NmEzMGFkNGMwOTY2MzM5OGI0NTVjOGQwNDI3MjY3MmQxOGIi.Xk6ReA.dFbMlM0bClY6BvAVjxZ0GfiFBM4
Then I submit a too short message using the comment reply form. The form is send to the server, rendered at the server and send back to the client.
Inspection showed that the CSRF token of the comment reply form changed:
comment_reply_form-csrf_token: IjM4N2Y3NmEzMGFkNGMwOTY2MzM5OGI0NTVjOGQwNDI3MjY3MmQxOGIi.Xk6SUg.6axMGCN2KmQQ4WeAAxYgTg6UdAs
Here the following questions came up:
- How is the CSRF token constructed?
- When does the CSRF token expire?
- Do different forms on a page require different CSRF token?
- How can the CSRF token of the comment reply form change?
- What is the X-CSRFToken?
How is the CSRF token constructed?
Time to start reading again. From the OWASP guide:
In general, developers need only generate this token once for the current session. After initial generation of this token, the value is stored in the session and is used for each subsequent request until the session expires.
In Flask-WTF the raw token is in the session and the signed token is in g. Or more accurate, the raw token is always in the session and the signed token is in g after a form has been used. Before instantiating a form:
session['csrf_token']: 387f76a30ad4c09663398b455c8d04272672d18b
g.csrf_token: None
After instantiating a form:
session['csrf_token']: 387f76a30ad4c09663398b455c8d04272672d18b
g.csrf_token: IjM4N2Y3NmEzMGFkNGMwOTY2MzM5OGI0NTVjOGQwNDI3MjY3MmQxOGIi.Xk6vnw.L7uNOptXpBxemh_TIEy3e9Ujllg
The signed token is put in g so that with subsequent requests, for example in case of multiple forms, the same signed token can be used and does not have to be generated again. If you want you can experiment with generating and validating tokens using the functions generate_csrf() and validate_csrf():
from flask_wtf.csrf import generate_csrf, validate_csrf
secret_key = current_app.config['SECRET_KEY']
# default: WTF_CSRF_FIELD_NAME = 'csrf_token'
token_key = current_app.config['WTF_CSRF_FIELD_NAME']
csrf_token = generate_csrf(secret_key=secret_key, token_key=token_key)
current_app.logger.debug(fname + ': csrf_token = {}'.format(csrf_token))
validated = False
try:
validate_csrf(csrf_token, secret_key=secret_key, time_limit=3600, token_key=token_key)
validated = True
except:
pass
current_app.logger.debug(fname + ': validated = {}'.format(validated))
The above functions do something like a black box. But we can see that the generated token is signed and includes a timestamp.
When does the CSRF token expire?
We already saw that the CSRF token expire time can be controlled with:
app.config['WTF_CSRF_TIME_LIMIT'] = 5
The default value is 3600, one hour. This time is somewhere in the token. There can be two possible ways how this is implemented:
- The start time is in the CSRF token
- The end time is in the CSRF token
This is not important to know unless you want to change the expire time inside your application. The generate_csrf() function does not include a time_limit parameter while the validate_csrf does. This would mean that the start time is in the CSRF token. To verify this I put a page with a form on the screen, then changed the default time_limit to 10 seconds and then submitted the form. As expected, a CSRF exception was raised.
There is another condition when your CSRF token expires and that is when the session expires. The reason is that the CSRF raw token is stored in the session. This means that if your session lives shorter than your CSRF token expire time you can get CSRF errors.
Do different forms on a page require different CSRF token?
Answer: This is more a general question. The answer is no. We can use the same CSRF token for all forms on a page.
How can the CSRF token of the comment reply form change?
Answer: The token does not change but the signed token changes, because there also is a timestamp in the signed token. If you look at the signed tokens above you notice that the first part is identical.
The X-CSRFToken header parameter
You may have read about the X-CSRFToken. The X-CSRFToken is a setting in the header. Usually we do not need this. If you have a CSRF token parameter in your AJAX POST then this will be used. If not then can set this in the header of the POST using jQuery:
var csrf_token = ...
...
$.ajax({
beforeSend: function(xhr, settings){
if(!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain){
xhr.setRequestHeader("X-CSRFToken", csrf_token);
}
},
It depends on your implementation if you send forms with a CSRF token parameter or if you set the X-CSRFToken header parameter. The documentation states you can only use this in ‘CSRF exception mode’ (adding the extra code). I did not check this.
Handling CSRF exceptions and before_request
I already handle HTTP error exceptions like 401 (Unauthorized), 403 (Forbidden), 404 (Not Found), 405 (Method Not Allowed), 500 (Internal Server Error) but how do we handle the CSRF exception? The CSRF error propagates to a 400 (Bad Request).
I created nice bells and whistles error pages showing the exceptions. I did this by relying on the fact that that before_request was called.
In before_request I process the incoming request and among other things, set the selected language accordingly.
Unfortunately by default before_request is NOT called on a CSRF exception. But there is a way around this. We can set:
app.config['WTF_CSRF_CHECK_DEFAULT'] = False
The result will be that before_request is called. Then in before_request, after some essential processing, we can call:
csrf.protect()
This will raise a CSFR exception, if there is one, and call the error handler accordingly.
Multi-language CSRF exception messages
I use Flask-Babel and validation messages are translated into the selected language. The CSRF exception messages did not translate and checking csrf.py it appeared that these messages are hard-coded in English(?). For the moment I created a dictionary to handle the translation:
csrf_error_message2translated_error_messages = {
...
'The CSRF token has expired.': _('The CSRF token has expired.'),
...
}
if error.description in error_description2error_messages:
error_message = error_description2error_messages[error.description]
else:
error_message = error.description
Responding to exceptions, including CSRF, on AJAX requests
With AJAX we cannot show our nice HTML error page on an exception. On a request we must return an error code or HTML that the client understands. It comes down to returning the exception as a JSON encoded error message to the client. In Flask we already have our error handlers.
But how do we know that the request was coming from an AJAX call? I am sending the form with encoding application/x-www-form-urlencoded:
$.ajax({
data: $('#' + form_id).serialize() + '&' + encodeURI( $(submit_button).attr('name') ) + '=' + encodeURI( $(submit_button).val() ),
type: 'POST',
url: url_comment_new,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8',
dataType: 'json'
})
One way is to look at the received Accept header.
current_app.logger.error(fname + ': request.accept_mimetypes = {}'.format(request.accept_mimetypes))
# text/javascript,application/json,*/*;q=0.01
From the document ‘List of default Accept values’:
Note that all browsers add the */* MIME Type to cover all cases. This is typically used for requests initiated via the address bar of a browser, or via an HTML <a> element.
This means that both ‘request.accept_mimetypes.accept_json’ and ‘request.accept_mimetypes.accept_html’ are True. Not very useful. The accept_mimetypes are listed in order of preference. We could scan the list and decide to return JSON when encountering ‘application/json’ before ‘text/html’. But I do not know exactly which browsers send which accept_mimetypes, meaning that I am not 100% sure at this moment that the ‘without AJAX’ error pages will not accidently be shown as JSON.
A much safer way is to add a parameter to AJAX requests and check in the error handler if this parameter is present. If it is present then we return a JSON error message, if it is not we output the standard error page. I do not like this but it works and some time will dig deeper into this.
Summary
You should always check if CSRF protection is working. I did not, and that was the start of very much reading. It appeared that are two ways to deal with CSRF protection, CSRF form error and CSRF exception. I choose the second one. For me, handling CSRF protection for this multilanguage site was not standard, the main reason being that before_request is not called by default on a CSRF exception. In before_request I check and set the language among other things so this really must run. Fortunately we can delay the CSRF protection and call it explicitly in before_request after essential processing using csrf.protect(). We already have our exception error pages but with AJAX requests we must return JSON error messages. Detecting if the request comes from an AJAX request is complex so I used an extra parameter in the AJAX requests.
Задача:
Найти причину возникновения ошибки “Missing or expired CSRF token” и варианты решения
—————————————————————
CSRF (Cross-Site Request Forgery) – это «межсайтовая подделка запроса» или ещё называют “подделка HTTP-запросов”, также известна как XSRF. Суть атаки заключается в том, что браузер пользователя отправляет сайту значение Cookie, т.е. запрос будет воспринят как исходящий от аутентифицированного пользователя.
В нашем случае надо знать версию pfSense. До версии 2.4.2, веб-интерфейс был уязвим для Clickjacking. Уязвимость имела название “CVE-2017-1000479” и позволяла злоумышленнику выполнить произвольный код с привилегиями root. Поэтому обновляемся.
Если у вас версия pfSense 2.4.2 и выше, то скорее всего вы были авторизованны в веб-интерфейсе администрирования и после работы не вышли.
Missing or expired CSRF token
Form session may have expired, cockies may not be enabled, or possible CSRF-based attack.
Resubmitting this request may put the firewall at risk or lead to unintended behavior.
I undestand this warning and wish to resubmit the form data.
Resubmit Request with New Token
Поэтому не забываем выходит “logout”. Сделать это можно через иконку в виде стрелочки вправо и расположенной в правой, верхней части окна веб-интерфейса.
Решений данной проблемы, у меня было два:
- Удалить кеш браузера и попробовать перезайти или воспользоваться другим браузером
- Поставить галочку напротив “I undestand this warning and wish to resubmit the form data.” и нажать “Resubmit Request with New Token”
Другие статьи
CSRF Token — это уникальный способ защититься от CSRF-атак, которые достаточно распространены в веб-приложениях. Вообще, веб — это среда, где очень много различных видов атак, от которых нужно защищаться.
Любая атака злоумышленников нацелена на получение собственной выгоды для атакующего и на нанесение ущерба атакуемого объекта. В результате любой атаки злоумышленник незаконно овладевает какой-то информацией, которую может использовать в собственных целях. Это может быть личная конфиденциальная информация пользователей:
платежные реквизиты;
личные сообщения;
доступы к разным аккаунтам;
и др.
Чтобы завладеть такой информацией, злоумышленники могут использовать разные способы, одним из которых является CSRF.
Что такое CSRF-атака?
CSRF — это «cross-site request forgery» и переводится как «межсайтовая подделка запросов». Этот вид атаки строится на «пробелах в безопасности» HTTP-запросов. Например, пользователь ничего не подозревает и заходит на какой-то сайт, который создал злоумышленник. Пока пользователь находится на зловредном сайте, злоумышленник отправляет от его лица запросы на сервера других веб-ресурсов. Например, хакер может от лица этого пользователя перевести деньги на собственный счет с пользовательского онлайн-банка. Самое главное, чтобы в этот момент пользователь был аутентифицирован на атакуемом ресурсе и там не требовалось подтверждение проводимых операций. К примеру, пользователь осуществлял оплату в интернет-банке, страница интернет-банка осталась открытой, а пользователь перешел на сайт злоумышленника.
Конечно, с интернет-банком такая процедура может не пройти, так как большинство банков требуют подтверждение финансовых операций, но в других случаях такой способ отлично работает на хакеров.
То есть CSRF — это способ провести операцию на уязвимом сайте от имени атакуемого пользователя. С помощью CSRF хакер может:
изменить пароль;
восстановить пароль;
поменять номер телефона на собственный;
поменять электронную почту на собственную;
добавить пользователя в аккаунт;
что-либо оплатить;
и др.
CSRF-атаки доступны по простой причине. Браузер во время сессии не способен точно определить кто выполняет некоторые действия: пользователь или программа. Например:
кто нажал на кнопку;
кто перешел по ссылке;
кто написал;
и др.
Эту проблему с безопасностью уже очень давно используют хакеры, но, благо, от нее можно защититься при помощи CSRF-токена.
CSRF-token — что это?
CSRF-token — это самый эффективный способ защититься от CSRF-атаки. Причем он не только эффективный, но и очень простой. Суть его в том, что сервер формирует случайный набор байт и отправляет их браузеру клиента. Набор байт — это и есть токен. Потом, когда браузер отправляет запрос серверу, происходит проверка, возвращает ли обратно браузер CSRF-token. Если токен, который генерировал сервер совпадает с тем, который вернул браузер, тогда все хорошо и сессия продолжается.
CSRF-token обычно защищает только небезопасные HTTP-запросы, например:
POST;
PUT;
DELETE;
PATCH.
Безопасные методы HTTP-запросов нет смысла дополнительно защищать токеном.
К CSRF-token применяются следующие требования:
должен быть уникальным для каждой отдельной операции;
должен действовать единоразово;
обладает размером, который обеспечивает ему устойчивость к подбору;
генерируется специальным криптографическим генератором случайных чисел;
имеет ограничение по времени жизни.
Виды CSRF-токенов
CSRF-token может генерироваться и использоваться тремя способами:
Synchronizer Tokens. Это самый простой и распространенный способ использования CSRF-токена. В этом случае токен генерируется сервером и передается браузеру, при этом «копия» токена сохраняется на сервере. Потом происходит сверка токена браузера и токена, который сохранен на сервере. Когда сессия между браузером и сервером обновляется, тогда обновляется и CSRF-token.
Double Submit Cookie. В таком методе токен не хранится на сервере, а передается браузеру в двух экземплярах: в куках и в каком-то параметре ответа. При первом запросе к серверу, сервер формирует токен. Потом сервер передает его обратно браузеру, но в двух местах: в куках и еще в каком-то месте. При следующем обращении к серверу, у браузера проверяется наличие и идентичность токенов в обоих местах.
Encrypted Token. В этом случае в качестве токена является конкретная информация о клиенте, но зашифрованная специальным ключом. То есть при первом обращении к серверу из какой-то информации о браузере генерируется токен и помещается в какой-то параметр ответа. При последующих обращениях браузер должен вернуть токен. Токен расшифровывается и информация из токена должна совпасть с информацией о браузере.
Заключение
CSRF-token — это рабочий способ защититься от CSRF-атак. Даже самый простой способ генерации CSRF-токена обеспечивает надежную защиту. А защитой, как известно, не нужно пренебрегать.
В настоящее время в сфере обеспечения безопасности веб-сайтов и приложений возникла очень интересная ситуация: с одной стороны, некоторые разработчики уделяют особое внимание безопасности, с другой, они напрочь забывают о некоторых видах атак и не считают ошибки, позволяющие выполнить данные атаки, уязвимостями. Например, к такой категории можно отнести CSRF (Сross Site Request Forgery). Эта атака позволяет производить различные действия на уязвимом сайте от имени авторизованного пользователя. Если вы не слышали о таком, то я рекомендую прочитать соответствующую статью в Википедии, чтобы иметь общее представление об этом виде атак. Основная часть статьи предназначена тем, кто обеспокоен правильной защитой своих сайтов от CSRF.
Замечание 1: если подходить формально, то CSRF является атакой, а не уязвимостью, как и XSS. Уязвимостью является неправильная обработка входных данных, а CSRF это использует.
Замечание 2: если какие-то ошибки показались вам очевидными и не заслуживающими упоминания, то я рад за вас. Однако данный материал основан на реальных уязвимостях крупных сайтов, а каждый пункт показывает ошибку какой-либо команды разработчиков, обернувшуюся дырой в безопасности.
Список ошибок:
1) Полностью отсутствует защита от CSRF.
По своему опыту могу сказать, что в настоящее время это — самая распространенная ошибка. Ее можно встретить как на малопосещаемых блогах, так и на крупных проектах. Единственная уважительная причина не использовать защиту от данного вида атак — сайт не хранит никакие пользовательские данные, а вы не используете панель администратора для редактирования материалов.
2) Защищены не все запросы.
Я бы поставил эту ошибку на второе место по распространенности. На многих сайтах, где реализована какая-либо защита от CSRF, можно найти уязвимые запросы. Например, если вы воспользуетесь поиском Хабра habrahabr.ru/search/?q=CSRF, то увидите значительное количество статей, повествующих о найденных уязвимостях на тех сервисах, где есть защита.
Вы должны защищать абсолютно все запросы, которые изменяют что-либо на сайте. Вы добавили токен в форму смены адреса электронной почты, и злоумышленник не сможет завладеть аккаунтом вашего пользователя, изменив от его имени почту, а затем и пароль? Здорово. Вот только такая мера бесполезна, если можно просто отправить запрос на перевод денег с аккаунта жертвы на кошелек атакующего, минуя вашу защиту.
Удобство обеспечения безопасности — одна из причин использовать только метод POST для запросов, изменяющих данные пользователя. Если вы следуете этому совету, то необходимо просто убедиться, что все POST-запросы содержат надежный и правильный токен. Об этом речь пойдет ниже.
3) Использование для защиты от CSRF чего-либо, кроме токенов.
Казалось бы, очень удобно использовать HTTP referer (https://ru.wikipedia.org/wiki/HTTP_referer) для защиты от атак. Если в этом заголовке не страницы с вашего домена, то запрос был подделан. Но не все так радужно. У небольшой части пользователей HTTP referer может быть пуст по разным причинам. Кроме того, он может быть подделан с использованием старых версий Flash, что подставляет под удар тех, кто очень долго ничего не обновлял на своем компьютере.
Как насчет использования капчи? Я слышал достаточно большое количество вопросов от разработчиков о возможности их использования для защиты от атаки. Мой однозначный ответ — нет. Во-первых, вы явно не будете заставлять пользователя вводить капчу на каждый чих: это приведет к ошибке № 2. Во-вторых, далеко не все способы реализации капч обеспечат вас должной защитой, которую злоумышленник не сможет обойти. Поскольку эта тема является весьма спорной и актуальной, в дальнейшем я посвящу ей отдельную статью.
Для защиты от CSRF вы должны использовать анти-CSRF токены и только их. Лишь они обеспечивают должную защиту ваших сайтов. В общих чертах о механизме токенов рассказано в Википедии:
4) Отсутствие проверки анти-CSRF токена при обработке запроса.
Подобную ошибку я встречал на сайтах весьма серьезных компаний, чья безопасность должна быть на высоте.
В самом запросе токен есть, а при его обработке он не проверяется. Можно вставить в это поле любую строку, запрос все равно будет корректно обработан. Комментировать тут особенно нечего, надо только указать, что применение функции isset() php.net/manual/ru/function.isset.php для проверки токена совершенно недопустимо.
5) Частичная проверка анти-CSRF токена.
Данную ошибку я встретил сразу на нескольких крупных сайтах рунета в разных вариациях. Например, один из сайтов использовал токены вида «Имя_пользователя.Текущее_время.Длинное_случайное_число». При этом проверялось только соответствие имени пользователя в токене и логина того, от чьего имени был отправлен запрос. Это немного усложняет атаку, но не делает ее невозможной.
6) Возможность использовать один токен для разных пользователей.
Данную ошибку я встретил один раз, но на достаточно крупном сайте, так что считаю необходимым упомянуть ее. Злоумышленник мог зарегистрировать новый аккаунт на сайте, скопировать токен из исходного кода страницы и использовать его для CSRF. Не допускайте такой ошибки, так как она полностью уничтожает все плюсы токенов на вашем сайте.
7) Недостаточная длина токена.
Ваш токен должен быть настолько длинным, чтобы злоумышленник потратил на его подбор как минимум столько же времени, сколько и на подбор пароля пользователя. Я встречал токены из 2 символов, они не сильно помогут, если кто-то очень сильно захочет осуществить CSRF-атаку.
Предсказумые токены.
При разработке алгоритма генерации токена обязательно используйте случайные данные в токене (совет актуален, если вы разрабатываете всю систему с нуля. В случае использования фреймворка или CMS вы должны полагаться на их разработчиков). Поверьте, токен вида «md5(user_id)» — очень плохая идея.
9) Отсутствие токенов в админ-панели или системе для сотрудников техподдержки.
Даже если весь доступный вашим пользователям сайт защищен от CSRF, то не стоит забывать про панель администратора. В одной известной в узких кругах биллинг-системе было много CSRF именно в панели администратора (хотя они были и в публичной части системы, но это не так важно). И любой, кто знал структуру запросов, мог использовать CSRF-атаку на сотрудника техподдержки и получить доступ к данным всех клиентов компании, использующей данную биллинг-систему. Единственная проблема — необходимо узнать структуру запросов: для этого можно использовать социальную инженерию, скачать копию в открытых источниках или просто взломать менее защищенный сайт, использующий такую же систему.
10) Передача токенов в открытом виде, особенно в GET-запросах.
На нескольких сайтах я видел ситуации, когда токены пользователей передавались в открытом виде: если токен содержится в адресе страницы, то пользователь может скопировать ссылку целиком и разместить ее где-нибудь, даже не подозревая об опасности. Вам не нужно сильно беспокоиться о скрытой передаче токенов только тогда, когда они одноразовые, а пользователь может случайно раскрыть только использованный токен. Однако это все равно не очень хорошо, так как сигнализирует о некоторых проблемах с архитектурой приложения: например, вы используете GET, а не POST для запросов, изменяющих пользовательские данные.
Наличие этих ошибок даже на крупных и серьезных сайтах показывает, что проблема защиты от CSRF-атак стоит достаточно остро. Безусловно, этот список не является исчерпывающим. Я уверен, что можно найти еще несколько ошибок и способов их эксплуатации. Однако если вы проверите свои сайты на наличие проблем, описанных в этой статье, и исправите их, то значительно повысите защищенность проекта.
Недопустимый токен CSRF. Пожалуйста, попробуйте отправить форму
Я получаю это сообщение об ошибке каждый раз, когда я пытаюсь представить форме:
недопустимый маркер CSRF. Пожалуйста, попробуйте повторно отправить форму
13 ответов
вам нужно добавить _token в форме я.е
на данный момент в вашей форме отсутствует поле токена CSRF. Если вы используете функции формы веточки для отображения вашей формы, как form(form) это автоматически отобразит поле токена CSRF для вас, но ваш код показывает, что вы визуализируете свою форму с raw HTML, как <form></form> , поэтому вам нужно вручную отобразить поле.
или просто добавьте > перед закрывающим тегом формы.
согласно docs
это отображает все поля, которые еще не были отображены для данного форма. Это хорошая идея, чтобы всегда иметь это где-то внутри формы. как он будет отображать скрытые поля для вас и сделать любые поля, которые вы забыли чтобы сделать более очевидным (так как он будет отображать поле для вас).
Также вы можете увидеть это сообщение об ошибке, если ваша форма имеет много элементов.
эта опция в php.ini причина проблемы
проблема в том, что _token пропускает запрос PUT (GET) Таким образом, вы можете увеличить стоимость.
кроме того, это касается больших файлов. Увеличение
опция решит проблему
это происходит потому, что формы по умолчанию содержат защиту CSRF, которая в некоторых случаях не требуется.
вы можете отключить эту защиту CSRF в своем классе формы в getDefaultOptions способ такой:
если вы не хотите отключать защиту CSRF, вам нужно отобразить поле защиты CSRF в вашей форме. Это можно сделать с помощью > в вашем файле представления, например:
> отображает все поля, которые вы не ввели вручную.
перед </form> tag put:
он автоматически вставит другие важные (скрытые) входы.
в дополнение к другим предложениям вы можете получить ошибки токена CSRF, если ваше хранилище сеансов не работает.
в недавнем случае мой коллега изменил «session_prefix» на значение, в котором было пробел.
это сломанное хранилище сеансов, что, в свою очередь, означало, что моя форма не смогла получить токен CSRF из сеанса.
У меня была эта проблема со странным поведением: очистка кэша браузера не исправила ее, но Очистка файлов cookie (то есть файла cookie идентификатора сеанса PHP) решила проблему.
Это должно быть сделано после вы проверили все другие ответы, включая проверку вас do имейте токен в скрытом поле ввода формы.
недавно у меня была эта ошибка. Оказывается, Мои настройки cookie были неправильными в config.в формате YML. Добавление cookie_path и cookie_domain параметры framework.session исправил.
Если вы не хотите использовать form_row или form_rest и просто хотите получить доступ к значению _token в шаблоне twig. Используйте следующее:
в моем случае у меня возникли проблемы с аннотацией maxSize в сущности, поэтому я увеличил ее с 2048 до 20048.
надеюсь, что этот ответ поможет!
если вы преобразовали свою форму из простого HTML в twig, убедитесь, что вы не пропустили удаление закрытия </form> — тег. Глупая ошибка, но как я обнаружил, это возможная причина этой проблемы.
когда я получил эту ошибку, я не мог понять сначала. Я использую form_start() и form_end() для генерации формы, поэтому я не нужно явно добавлять маркер С form_row(form._token) , или использовать form_rest() чтобы получить его. он уже должен был быть добавлен автоматически form_end() .
проблема заключалась в том, что представление, с которым я работал, было преобразовано из простого HTML в twig, и я пропустил удаление закрытия </form> — тег, поэтому вместо :
это на самом деле похоже на то, что может вызвать ошибку, но, по-видимому, это не так, поэтому когда form_end() выходы form_rest() , форма уже закрыта. Фактический сгенерированный источник страницы формы был похож это:
очевидно, решение удалить лишний закрывающий тег и может выпить еще кофе.
Я столкнулся с аналогичной проблемой. После того, как поле токена было фактически отображено (см. принятый ответ), я проверил свои куки. Было 2(!) cookies для домена в моем браузере Chrome, по-видимому, потому, что я запускал приложение в том же домене, что и другое приложение, но с другим портом (т. е. mydomain.com установите исходный файл cookie, пока приложение buggy работает на mydomain.com: 123) Теперь, по-видимому, Chrome отправил неправильный cookie, поэтому защита CSRF не смогла связать токен с правильный сеанс.
Fix: очистите все куки для рассматриваемого домена, убедитесь, что вы не запускаете несколько приложений в одном домене с разными портами.
у меня была та же ошибка, но в моем случае проблема заключалась в том, что мое приложение использовало несколько доменов первого уровня, в то время как cookie использовал один. Удаление cookie_domain: «.%domain%» с framework.session на config.yml вызвал cookies по умолчанию для любого домена, в котором была форма, и это исправило проблему.
это кажется проблемой при использовании bootstrap, если вы не визуализируете форму с помощью >. Кроме того, проблемы, похоже, возникают только при вводе type=»hidden». Если вы проверите страницу с формой, вы обнаружите, что скрытый ввод не является частью разметки вообще или он визуализируется, но не передается по какой-либо причине. Как было предложено выше, добавление > или упаковка ввода, как показано ниже, должны сделать трюк.
Что означает ошибка «CSRF токен истек»
Если вы столкнулись с ошибкой «истек CSRF-токен» — читайте нашу статью. Из неё вы узнаете, как работает CSRF-token защита, и что делать, если CSRF токен истек.
Что такое CSRF
CSRF (англ. cross-site request forgery) — это межсайтовая подделка запроса. Это атака, которой может подвергаться любой веб-ресурс или веб-приложение. В первую очередь это касается сайтов, которые используют cookies, сертификаты авторизации и браузерную аутентификацию. В результате атаки страдают клиенты и репутация ресурса.
Вредоносный скрипт прячется в коде сайта или обычной ссылке. С помощью него мошенник получает доступ к конфиденциальной информации: платежным реквизитам, логину и паролю, личной переписке. После того как данные “в кармане”, хакер может изменить пароль, указать свой номер телефона или email, перевести деньги на свой счёт и многое другое.
Как работает CSRF-атака
Злоумышленник может использовать фишинговую ссылку — это наиболее распространенный способ обмана. В этом случае атака работает по следующей схеме:
- Злоумышленник создаёт поддельную страницу, очень похожую на оригинальную, и встраивает её в сайт. В коде ссылка может выглядеть так: <a href=“вредоносная ссылка”>Unsubscribe here</a>.
- Пользователь переходит с одной страницы сайта на другую (например, на страницу оплаты) и вместо реальной страницы попадает на поддельную.
- Пользователь совершает действие на странице, например, оплачивает товар или вводит данные авторизации.
- Информация или денежные средства вместо оригинального сервера уходят на сервер мошенника.
CSRF-атаки случаются из-за того, что без специальных настроек сервер не может с точностью в 100% определить, кто именно выполняет действия со стороны пользователя. Он не может проверить, действительно ли на кнопку “оплатить” нажал тот пользователь, который изначально открыл страницу с оплатой. Хакеры активно используют этот люфт в безопасности HTTP-запросов и применяют вредоносные скрипты. Однако от атаки можно защититься с помощью CSRF-токенов.
Что такое CSRF-token и как он работает
В общем понимании токен — это механизм, который позволяет идентифицировать пользователя или конкретную сессию для безопасного обмена информацией и доступа к информационным ресурсам. Токены помогают проверить личность пользователя (например, клиента, который онлайн получает доступ к банковскому счёту). Их используют как вместо пароля, так и вместе с ним. Токен — это в каком-то смысле электронный ключ.
CSRF-token — это максимально простой и результативный способ защиты сайта от CSRF-мошенников. Он работает так: сервер создаёт случайный ключ (он же токен) и отправляет его браузеру клиента. Когда браузер запрашивает у сервера информацию, сервер, прежде чем дать ответ, требует показать ключ и проверяет его достоверность. Если токен совпадает, сессия продолжается, а если нет — прерывается. Токен действителен только одну сессию — с новой сессией он обновляется.
Чтобы получить ответ от сервера, используются разные методы запроса. Условно они делятся на две категории: те, которые не изменяют состояние сервера (GET, TRACE, HEAD), и те, которые изменяют (PUT, PATCH, POST и DELETE). Последние имеют большую CSRF-уязвимость и поэтому должны быть защищены в первую очередь.
При создании и использовании токена должны соблюдаться следующие условия:
нахождение в скрытом параметре;
генерация с помощью генератора псевдослучайных чисел;
ограниченное время жизни (одна сессия);
уникальность для каждой транзакции;
устойчивый к подбору размер (в битах);
Типы токенов
Существует три основных типа токенов по способу генерации:
- Synchronizer Tokens или Anti-CSRF (токены синхронизации). В этом случае инициатором ключа выступает сервер — на нём хранится исходная шифровка. Когда браузер обращается к серверу и предъявляет ему ключ, сервер сравнивает его с исходником и в зависимости от результата продолжает или прерывает сессию.
- Double Submit Cookie (двойная отправка куки). При этом способе токен нигде не хранится. Когда браузер обращается к серверу впервые за сессию, сервер генерирует и передаёт ему ключ в двух формах: через куки и в одном из параметров ответа. При следующих обращениях браузера сервер дважды проверяет правильность ключа — в параметрах и в куках.
- Encrypted Token (зашифрованный токен). Этот способ предполагает, что ключом шифруется какая-то часть информации о клиенте, которая содержится в браузере. При первом запросе браузера сервер получает информацию о пользователе, зашифровывает её и передаёт браузеру токен. При следующем взаимодействии сервер расшифровывает токен и сверяет информацию.
Помимо токенов, для защиты используется флаг Same-Site (большинство браузеров его поддерживает). Он работает напрямую для cookies и позволяет помечать куки конкретного домена. Сервер проверяет, содержатся ли нужные пометки в куках страницы, с которых происходит оплата или вносятся изменения. Если пометок нет — сессия прекращается.
Также в качестве меры защиты на страницах сайта настраивают форму с капчей. Это особенно актуально для страниц смены пароля или совершения денежных транзакций.
«Истек срок действия токена» или «CSRF-значение недопустимо»: что это значит и что делать
Даже при авторизации на сайтах, для которых настроена защита от атак, можно встретить следующие варианты сообщения об ошибке: «Недопустимое CSRF-значение»/«CSRF-токены не совпадают» или «Token expired» (в переводе — срок действия токена истек). Сообщение может отображаться как на английском, так и на русском. Пример ошибки при авторизации на сайте REG.RU:
Обычно ошибка возникает по двум основным причинам:
сервер некорректно сгенерировал токен;
срок токена истек — пользователь долго не совершал никаких действий на странице.
В обоих случаях исправить проблему поможет перезагрузка страницы — вы запустите новую сессию, а значит, сервер и браузер договорятся о новом рабочем токене. Для этого нажмите на значок обновления страницы:
Иногда ошибка возникает из-за расширений защиты конфиденциальности или плагинов блокировки рекламы (например, Ghostery, UBlock Origin, Blur), которые настроены у пользователя. В этом случае можно отключить расширение. Также можно добавить сайт, на котором появилось сообщение, в список доверенных сайтов.
На примере сайта reg.ru покажем, что для этого нужно:
- Откройте настройки Chrome:
- В списке слева выберите Конфиденциальность и безопасность, а затем Файлы cookie и другие данные сайтов.
- Внизу страницы откройте Сайты, которые всегда могут использовать файлы cookie и кликните Добавить.
- Введите «[*.]www.reg.ru» и нажмите Добавить.
- Нажмите Все файлы cookie и данные сайта и удалите все записи, которые связаны с сайтом reg.ru.
- Перезагрузите браузер и выполните операцию повторно.
- Откройте настройки браузера Яндекс:
- Перейдите на Сайты — Расширенные.
- Кликните Настройки… для первого параметра в списке. Затем на вкладке «Разрешена» введите www.reg.ruи кликните Добавить.
- Добавьте адрес сайта для всех параметров списка по аналогии.
- Откройте настройки Safari комбинацией Cmd + , (⌘,).
- Перейдите на вкладку Конфиденциальность и проверьте, что в пункте «Файлы cookie и данные веб-сайтов» не выбрано «Блокировать все файлы cookie». Если это так, снимите настройки.
- Кликните Управление данными веб-сайтов и удалите все записи, которые относятся к www.reg.ru.
- Перезагрузите браузер и выполните операцию повторно.
В некоторых случаях сгенерировать верный токен мешают локальные настройки куки в браузере. Чтобы сессия прошла успешно, достаточно вернуть дефолтные настройки.
Заключение
Успешная атака CSRF позволяет хакеру действовать на сайте от имени другого зарегистрированного посетителя. Чтобы мошенник не добрался до конфиденциальных данных, для сайта нужно настроить один из типов CSRF-токенов. Токены позволяют серверу и браузеру безопасно обмениваться информацией в течение сессии. Однако даже на безопасных сайтах можно столкнуться с ошибкой «токен CSRF истек». В этом нет ничего страшного. Чтобы возобновить подключение, достаточно обновить страницу браузера.
I can confirm that I am encountering this issue too. However I am not able to build a snippet for reproducing the bug every time.
If I am filling a faulty form in a private tab, the issue does not appear. Can you confirm that?
What wtforms version are you using?
edit: My website answers to two domains, strangely one is encountering this issue, and the other is not. On the failing domain, there are two session cookies, one which domain is .domain1.tld
and another one which domain is domain1.tld
, without the starting dot. On the succeeding domain, there is only one session cookie, which domain is domain2.tld
, without the starting dot.
edit2: In the failing domain, I can see that every form submission passes through this line. It feels like session['csrf_token']
is never written, or deleted somewhere else, or written in th.
https://github.com/lepture/flask-wtf/blob/c0e42ac78c347d0d26496a8903b854f24f1b3107/flask_wtf/csrf.py#L46-L47
edit3: This issue may be related. It seems this issue is encountered when several session cookies are involved.
edit4: The requests set-cookie
header content matches the session id of the cookie whose domain is domain1.tld
but the response Cookie
header matches has both session ids, the first one being the session id of the cookie whose domain is .domain1.tld
and the second one being the session id of the cookie whose domain is domain.tld
.
I’m using CodeIgniter 2 along with the Ion Auth authorization system by Ben Edmunds.
After creating my project, I would sometimes get a CodeIgniter error upon certain login attempts but this error was intermittent.
The action you have requested is not allowed.
After some troubleshooting, it became apparent this error was caused by an invalid CSRF token. Why is the token invalid? Well, in CodeIgniter’s configuration file, it’s set to expire in 4 hours. So if you load your login page and allow it to sit there for 4 hours before attempting a login, the CSRF tokens will expire and this will generate the error message as above. Simply reloading the login page avoids any issues.
You can verify this error message for yourself by deleting the CSRF cookie after you load the login page.
A cleaner solution would be to redirect to a custom error page or to display a flash message. However, this solution is not as simple as it sounds because when you extend the CodeIgniter Security class, certain hook-points are not available and you cannot yet access CodeIgniter’s Super Object using `get_instance()`.
So when you extend the Security class, you’re limited to standard PHP. In this case, I’m using PHP `header()` to redirect the offending login page (or any form page) back to itself.
<?php class MY_Security extends CI_Security { public function __construct() { parent::__construct(); } public function csrf_show_error() { // show_error('The action you have requested is not allowed.'); // default code // force page "refresh" - redirect back to itself with sanitized URI for security // a page refresh restores the CSRF cookie to allow a subsequent login header('Location: ' . htmlspecialchars($_SERVER['REQUEST_URI']), TRUE, 200); } }
This works fine except that the user gets a screen refresh without any indication why they have to enter their login credentials a seconds time.
I decided to make this a bit more user-friendly by adding another function into a Controller, in my case, the Ion Auth controller…
function csrf_redirect() { $flash = 'Session cookie automatically reset due to expired browser session. Please try again.'; $this->session->set_flashdata('message', $flash); redirect('/login', 'location'); }
As you can see, this function sets a flash message telling the user what happened and then redirects them to a fresh instance of the login page.
Session cookie automatically reset due to expired browser session. Please try again.
Instead of using PHP `header()` to redirect a page refresh, redirect to this new Ion Auth controller function at `/auth/csrf_redirect`.
<?php class MY_Security extends CI_Security { public function __construct() { parent::__construct(); } public function csrf_show_error() { // show_error('The action you have requested is not allowed.'); // default code // force redirect to the csrf_redirect function // this gives the user a useful message instructing them to login again // while the CSRF cookie is also refreshed to allow a new login header('Location: /auth/csrf_redirect', TRUE, 302); } }
The minor downside to this method is that you are always redirected back to the login page rather than a refresh of whatever page/form you’re trying to submit. However, that should be a moot point, since the session cookie expires at nearly the same time as the CSRF cookie, you’d be redirected back to the same login page regardless. You may also not be requiring the user be logged in for your particular form, so please be aware and re-direct accordingly.