Ошибка invalid csrf token битрикс 24

Вы можете отслеживать заказы, оплату и отгрузку товаров с магазина «1С-Битрикс: Управление сайтом» в вашем Битрикс24 с помощью специального приложения Обмен с 1С-Битрикс Управление сайтом. Для такой интеграции необходимо настроить сервер, на котором работает сайт с магазином.

Как это сделать

На большинстве серверах запрещено показывать страницы сайта в iframe-ах на сторонних ресурсах.

Вам нужно создать правило на сервере, которое разрешает открывать страницы в iframe на сторонних сайтах. Делается это с помощью добавления специального HTTP-заголовка Content-Security-Policy к страницам административного раздела:

/bitrix/admin/sale_order.php
/bitrix/admin/sale_order_create.php
/bitrix/admin/sale_order_edit.php
/bitrix/admin/sale_order_view.php
/bitrix/admin/sale_order_payment_edit.php
/bitrix/admin/sale_order_shipment_edit.php
/bitrix/admin/sale_delivery_request_view.php
/bitrix/admin/sale_delivery_request.php
/bitrix/admin/sale_app_rest_sender.php

Например, в «1C-Битрикс: Виртуальная машина» версии 7.4.4 сделать это можно так:

  1. Создать файл /etc/nginx/bx/site_avaliable/bx_exclude_x_frame.conf с таким содержимым (замените адрес вашего Битрикс24 в коде):
    location ~* ^/bitrix/admin/sale_(order|delivery|app_rest_sender).*.php$ {
    	add_header Content-Security-Policy "frame-ancestors https://адрес_вашего_Битрикс24;";
    	proxy_pass $proxyserver;
    }

    Обратите внимание, что в целях безопасности мы указываем конкретные адреса Битрикс24, которым нужно разрешить доступ к страницам магазина в iframe – например https://portal1.bitrix24.ru. Если нужно дать доступ нескольким Битрикс24, то их адреса указываются через пробел. Можно указать только домен, а можно и конкретный адрес (например https://portal1.bitrix24.ru/section/page.php).

  2. Найти 2 конфигурационных файла вашего сайта в директории /etc/nginx/bx/site_avaliable/.

    Например, для сайта mysite.ru файлы настроек могут быть такие:

    bx_ext_mysite.ru.conf – файл настроек сайта при http-подключении.
    bx_ext_ssl_mysite.ru.conf – файл настроек сайта при https-подключении.

    И прописать в секцию server каждого файла код:

     include bx/site_avaliable/bx_exclude_x_frame.conf; 
  3. Проверить, нет ли ошибок в конфигурационных файлах nginx:
     nginx -t 
  4. Если все хорошо, далее нужно перезагрузить конфигурационные файлы nginx:

    CentOS 6:

     service nginx reload 

    CentOS 7:

     systemctl reload nginx.service 

Если вы используете «1C-Битрикс: Виртуальная машина» версии 7.5 и выше , то достаточно всего лишь создать файл /etc/nginx/bx/site_settings/<SITE_NAME>/bx_exclude_x_frame.conf с тем же содержимым из п.1. После перезапуска nginx настройки из этого файла применятся к сайту <SITE_NAME>.

Например, для сайта mysite.ru путь к файлу будет таким: /etc/nginx/bx/site_settings/mysite.ru/bx_exclude_x_frame.conf.

Если у вас свое окружение или хостинг, обратитесь к администратору или в поддержку вашего хостинга.

Проверьте также, включена ли у вас защита от фреймов в продукте «1С-Битрикс: Управление сайтом». Для этого в административной панели перейдите в раздел Настройки > Проактивная защита > Защита от фреймов. Если у вас включена защита от фреймов, то внесите страницы интеграции в Исключения:

Защита от фреймов: исключения

Ошибки и их решение

Ошибка «Invalid csrf token» может возникать из-за значения параметра SameSite в куки PHPSESSID – по умолчанию он имеет значение Lax.

Для решения проблемы нужно обновить php до версии 7.3 и выше (php 7.4 рекомендуется) и прописать в php.ini параметры:

session.cookie_secure = On
session.cookie_samesite="None"

Если нет возможности включить данные параметры в php.ini, то попробуйте прописать похожие параметры в файл .htaccess:

Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure;SameSite=None

Обращение к сайту при этом должно быть только по HTTPS.

Советуем прочитать:

  • Курс «Виртуальная машина VMBitrix v7.x».
  • Проактивная защита.

Спасибо, помогло!

Спасибо :)


Это не то, что я ищу


Написано очень сложно и непонятно


Есть устаревшая информация


Слишком коротко, мне не хватает информации


Мне не нравится, как это работает

Вы можете отслеживать заказы, оплату и отгрузку товаров с магазина «1С-Битрикс: Управление сайтом» в вашем Битрикс24 с помощью специального приложения Обмен с 1С-Битрикс Управление сайтом. Для такой интеграции необходимо настроить сервер, на котором работает сайт с магазином.

Как это сделать

На большинстве серверах запрещено показывать страницы сайта в iframe-ах на сторонних ресурсах.

Вам нужно создать правило на сервере, которое разрешает открывать страницы в iframe на сторонних сайтах. Делается это с помощью добавления специального HTTP-заголовка Content-Security-Policy к страницам административного раздела:

/bitrix/admin/sale_order.php
/bitrix/admin/sale_order_create.php
/bitrix/admin/sale_order_edit.php
/bitrix/admin/sale_order_view.php
/bitrix/admin/sale_order_payment_edit.php
/bitrix/admin/sale_order_shipment_edit.php
/bitrix/admin/sale_delivery_request_view.php
/bitrix/admin/sale_delivery_request.php
/bitrix/admin/sale_app_rest_sender.php

Например, в «1C-Битрикс: Виртуальная машина» версии 7.4.4 сделать это можно так:

  1. Создать файл /etc/nginx/bx/site_avaliable/bx_exclude_x_frame.conf с таким содержимым (замените адрес вашего Битрикс24 в коде):
    location ~* ^/bitrix/admin/sale_(order|delivery|app_rest_sender).*.php$ {
    	add_header Content-Security-Policy "frame-ancestors https://адрес_вашего_Битрикс24;";
    	proxy_pass $proxyserver;
    }

    Обратите внимание, что в целях безопасности мы указываем конкретные адреса Битрикс24, которым нужно разрешить доступ к страницам магазина в iframe – например https://portal1.bitrix24.ru. Если нужно дать доступ нескольким Битрикс24, то их адреса указываются через пробел. Можно указать только домен, а можно и конкретный адрес (например https://portal1.bitrix24.ru/section/page.php).

  2. Найти 2 конфигурационных файла вашего сайта в директории /etc/nginx/bx/site_avaliable/.

    Например, для сайта mysite.ru файлы настроек могут быть такие:

    bx_ext_mysite.ru.conf – файл настроек сайта при http-подключении.
    bx_ext_ssl_mysite.ru.conf – файл настроек сайта при https-подключении.

    И прописать в секцию server каждого файла код:

     include bx/site_avaliable/bx_exclude_x_frame.conf; 
  3. Проверить, нет ли ошибок в конфигурационных файлах nginx:
     nginx -t 
  4. Если все хорошо, далее нужно перезагрузить конфигурационные файлы nginx:

    CentOS 6:

     service nginx reload 

    CentOS 7:

     systemctl reload nginx.service 

Если вы используете «1C-Битрикс: Виртуальная машина» версии 7.5 и выше , то достаточно всего лишь создать файл /etc/nginx/bx/site_settings/<SITE_NAME>/bx_exclude_x_frame.conf с тем же содержимым из п.1. После перезапуска nginx настройки из этого файла применятся к сайту <SITE_NAME>.

Например, для сайта mysite.ru путь к файлу будет таким: /etc/nginx/bx/site_settings/mysite.ru/bx_exclude_x_frame.conf.

Если у вас свое окружение или хостинг, обратитесь к администратору или в поддержку вашего хостинга.

Проверьте также, включена ли у вас защита от фреймов в продукте «1С-Битрикс: Управление сайтом». Для этого в административной панели перейдите в раздел Настройки > Проактивная защита > Защита от фреймов. Если у вас включена защита от фреймов, то внесите страницы интеграции в Исключения:

Защита от фреймов: исключения

Ошибки и их решение

Ошибка «Invalid csrf token» может возникать из-за значения параметра SameSite в куки PHPSESSID – по умолчанию он имеет значение Lax.

Для решения проблемы нужно обновить php до версии 7.3 и выше (php 7.4 рекомендуется) и прописать в php.ini параметры:

session.cookie_secure = On
session.cookie_samesite="None"

Если нет возможности включить данные параметры в php.ini, то попробуйте прописать похожие параметры в файл .htaccess:

Header edit Set-Cookie ^(.*)$ $1;HttpOnly;Secure;SameSite=None

Обращение к сайту при этом должно быть только по HTTPS.

Советуем прочитать:

  • Курс «Виртуальная машина VMBitrix v7.x».
  • Проактивная защита.

Спасибо, помогло!

Спасибо :)

Необязательно:

Оставить отзыв о статье

Уточните, пожалуйста, почему:

Это не то, что я ищу

Очень сложно и непонятно

Оставить отзыв о статье

У меня на странице есть форма с неколькими полями и кнопкой. При нажатии на кнопку данные должны отправляться на сервер и там обрабатываться. Использую стандартные asp контролы: asp:TextBox и asp:Button. В серверной части обрабатываю событие OnClick у кнопки.
Если страница не кеширована, то все нормально. Если же нет, то возникает ошибка Provided security token is invalid!

Provided security token is invalid!
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: Bitrix.Security.BXCsrfTokenValidationException: Provided security token is invalid!

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[BXCsrfTokenValidationException: Provided security token is invalid!]
  Bitrix.Security.BXCsrfToken.ValidateToken(String token) +92
  Bitrix.Security.BXCsrfToken.ValidatePost() +227
  Bitrix.UI.TemplateRequisite.PageInitComplete(Object sender, EventArgs e) +53
  System.EventHandler.Invoke(Object sender, EventArgs e) +0
  System.Web.UI.Page.OnInitComplete(EventArgs e) +11032014
  Bitrix.UI.BXPublicPage.OnInitComplete(EventArgs e) +16
  System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1674

Кто знает как побороть это на кешированных страницах?
Кеширование настроено так:
<%@ OutputCache Duration=»420″ Location=»ServerAndClient» VaryByParam=»*» %>

Контроллеры — это часть MVC архитектуры, которая отвечает за обработку запроса и генерирование ответа. Сразу оговоримся, что дальше речь пойдет про компоненты-контроллеры в контексте Bitrix Framework.

Битрикс-контроллер принимает от клиента запрос и возвращает JSON с результатом или ошибкой. Классы-контроллеры содержат одно или несколько методов-действий и являются надстройкой над обычными компонентами, поэтому их нужно размещать в файле class.php компонента.

Бекенд

Тут все просто: в компоненте нужно реализовать интерфейс Controllerable, в ктором есть метод configureActions(). Этот метод возвращает массив с действиями которые можно вызвать. Например, для действия send нужно будет создать метод sendAction.

Метод-действие возвращает массив, который затем отдается клиенту в виде JSON.

Если требуется отдавать на сторону клиента ошибки, то нужно реализовать интерфейс Errorable, в котором есть методы getErrors() и getErrorByCode(). Также нужно будет реализовать коллекцию ошибок ErrorCollection, ее удобнее всего создать в методе onPrepareComponentParams().

В качестве простого примера создадим контроллер для формы обратной связи. Компонент пусть называется machaon:feedback, код контроллера разместится в файле /local/components/machaon/feedback/class.php.

<?php
//local/components/machaon/feedback/class.php

namespace MachaonComponents;

use BitrixMainError;
use BitrixMainErrorable;
use BitrixMainErrorCollection;
use BitrixMainEngineActionFilter;
use BitrixMainEngineContractControllerable;
use CBitrixComponent;

class FeedbackComponent extends CBitrixComponent implements Controllerable, Errorable
{
    protected ErrorCollection $errorCollection;

    public function onPrepareComponentParams($arParams)
    {
        $this->errorCollection = new ErrorCollection();
        return $arParams;
    }

    public function executeComponent()
    {
        // Метод не будет вызван при ajax запросе
    }

    public function getErrors(): array
    {
        return $this->errorCollection->toArray();
    }

    public function getErrorByCode($code): Error
    {
        return $this->errorCollection->getErrorByCode($code);
    }

    // Описываем действия
    public function configureActions(): array
    {
        return [
            'send' => [
                'prefilters' => [
                    // здесь указываются опциональные фильтры, например:
                    new ActionFilterAuthentication(), // проверяет авторизован ли пользователь
                ]
            ]
        ];
    }

    // Сюда передаются параметры из ajax запроса, навания точно такие же как и при отправке запроса.
    // $_REQUEST['username'] будет передан в $username, $_REQUEST['email'] будет передан в $email и т.д.
    public function sendAction(string $username = '', string $email = '', string $message = ''): array
    {
        try {
            $this->doSomeWork();
            return [
                "result" => "Ваше сообщение принято",
            ];
        } catch (ExceptionsEmptyEmail $e) {
            $this->errorCollection[] = new Error($e->getMessage());
            return [
                "result" => "Произошла ошибка",
            ];
        }
    }
}

Фронтенд

В js-библиотеке Битрикс уже есть функция для отправки запросов. Далее простой пример, где machaon:feedback это имя компонента, send — имя действия, а в data: {} передаются необходимые данные:

BX.ajax.runComponentAction("machaon:feedback", "send", {
    mode: "class",
    data: {
        "email": "vasya@email.tld",
        "username": "Василий",
        "message": "Где мой заказ? Жду уже целый час!"
    }
}).then(function (response) {
    // обработка ответа
});

Если все хорошо, с бэкенда нам вернется:

{
    "status": "success",
    "data": {
        "result": "Письмо отправлено"
    },
    "errors": []
}

Либо сообщение об ошибке:

{
    "status": "error",
    "data": {
        "result": "Произошла ошибка"
    },
    "errors": [{
        "message": "Не заполено поле Email",
        "code": 0,
        "customData": null
    }]
}

Отправка запроса напрямую

Если не хочется использовать BX.ajax.runComponentAction(), можно отправить запрос напрямую, например используя jQuery. Нужно отправить запрос на /bitrix/services/main/ajax.php, он будет выглядеть примерно так:

$.post(
    "/bitrix/services/main/ajax.php?mode=class&c=machaon:feedback&action=send",
    {
        "email": "vasya@email.tld",
        "username": "Василий",
        "message": "Где мой заказ? Жду уже целый час!"
    },
    function (response) {
        console.log(response);
    }
);

В параметре mode передается обязательное значение class, в c передается имя компонента в формате vendor:component, action это запускаемый метод.

Эти параметры обязательно должны передаваться в адресе запроса, иначе происходит ошибка.

email, username и message станут затем параметрами $email, $username и $message в методе sendAction().

Использование ЧПУ

Также можно в urlrewrite.php прописать красивый адрес, например /api/rest-component/<component_vendor>/<component_name>/<action>/.

Для этого добавим в urlrewrite.php следующий массив:

$arUrlRewrite = [
    // ...
    [
        "CONDITION" => "#^/api/rest-component/([a-zA-Z0-9]+)/([a-zA-Z0-9.]+)/([a-zA-Z0-9]+)/?.*#",
        "RULE" => "mode=class&c=$1:$2&action=$3",
        "PATH" => "/bitrix/services/main/ajax.php",
    ],
];

Тогда запрос будет выглядеть понятнее:

function sendFeedback(form) {
    const route = "/api/rest-component/machaon/feedback/send/";
    const data = $(form).serialize();
    $.post(route, data, function (response) {
        console.log(response);
    });
}

Нюансы

Если в configureActions() оставить пустой массив prefilters, то метод-действие будет работать без фильтров.

Но если не указывать ключ prefilters, то будут применены фильтры по умолчанию — ActionFilterAuthentication и ActionFilterCsrf.

public function configureActions(): array
{
    return [
        'send' => [
            'prefilters' => [] // метод sendAction() будет работать без фильтров
        ]
    ];
}
public function configureActions(): array
{
    return [
        'send' => []  // метод sendAction() будет работать у авторизованных пользователей, которые передали CSRF-токен
    ];
}

Пример ошибки когда пользователь не авторизован и не передал токен:

{
  "status": "error",
  "data": null,
  "errors": [
    {
      "message": "Необходимо авторизоваться на сайте",
      "code": "invalid_authentication",
      "customData": null
    },
    {
      "message": "Invalid csrf token",
      "code": "invalid_csrf",
      "customData": {
        "csrf": "dc8adda3ac983217623cf1196dfc5c61"
      }
    }
  ]
}

Если требуется CSRF-защита, то для фильтра BitrixMainEngineActionFilterCsrf нужно передать идентификатор сессии в параметре sessid.

На бэкенде его можно получить функцией bitrix_sessid(), на фронтенде — BX.bitrix_sessid().

При использовании BX.ajax.runComponentAction() сессия передается автоматически.

Другие фильтры можно посмотреть в документации

Если вы столкнулись с ошибкой «истек CSRF-токен» — читайте нашу статью. Из неё вы узнаете, как работает CSRF-token защита, и что делать, если CSRF токен истек.


Ошибка токен истек 1

CSRF (англ. cross-site request forgery) — это межсайтовая подделка запроса. Это атака, которой может подвергаться любой веб-ресурс или веб-приложение. В первую очередь это касается сайтов, которые используют cookies, сертификаты авторизации и браузерную аутентификацию. В результате атаки страдают клиенты и репутация ресурса.

Вредоносный скрипт прячется в коде сайта или обычной ссылке. С помощью него мошенник получает доступ к конфиденциальной информации: платежным реквизитам, логину и паролю, личной переписке. После того как данные “в кармане”, хакер может изменить пароль, указать свой номер телефона или email, перевести деньги на свой счёт и многое другое.

Как работает CSRF-атака

Злоумышленник может использовать фишинговую ссылку — это наиболее распространенный способ обмана. В этом случае атака работает по следующей схеме:

  1. Злоумышленник создаёт поддельную страницу, очень похожую на оригинальную, и встраивает её в сайт. В коде ссылка может выглядеть так: <a href=“вредоносная ссылка”>Unsubscribe here</a>.
  2. Пользователь переходит с одной страницы сайта на другую (например, на страницу оплаты) и вместо реальной страницы попадает на поддельную.
  3. Пользователь совершает действие на странице, например, оплачивает товар или вводит данные авторизации.
  4. Информация или денежные средства вместо оригинального сервера уходят на сервер мошенника.

CSRF-атаки случаются из-за того, что без специальных настроек сервер не может с точностью в 100% определить, кто именно выполняет действия со стороны пользователя. Он не может проверить, действительно ли на кнопку “оплатить” нажал тот пользователь, который изначально открыл страницу с оплатой. Хакеры активно используют этот люфт в безопасности HTTP-запросов и применяют вредоносные скрипты. Однако от атаки можно защититься с помощью CSRF-токенов. 

Что такое CSRF-token и как он работает

В общем понимании токен — это механизм, который позволяет идентифицировать пользователя или конкретную сессию для безопасного обмена информацией и доступа к информационным ресурсам. Токены помогают проверить личность пользователя (например, клиента, который онлайн получает доступ к банковскому счёту). Их используют как вместо пароля, так и вместе с ним. Токен — это в каком-то смысле электронный ключ.

CSRF-token — это максимально простой и результативный способ защиты сайта от CSRF-мошенников. Он работает так: сервер создаёт случайный ключ (он же токен) и отправляет его браузеру клиента. Когда браузер запрашивает у сервера информацию, сервер, прежде чем дать ответ, требует показать ключ и проверяет его достоверность. Если токен совпадает, сессия продолжается, а если нет — прерывается. Токен действителен только одну сессию — с новой сессией он обновляется.

Чтобы получить ответ от сервера, используются разные методы запроса. Условно они делятся на две категории: те, которые не изменяют состояние сервера (GET, TRACE, HEAD), и те, которые изменяют (PUT, PATCH, POST и DELETE). Последние имеют большую CSRF-уязвимость и поэтому должны быть защищены в первую очередь.

При создании и использовании токена должны соблюдаться следующие условия:

  • нахождение в скрытом параметре;

  • генерация с помощью генератора псевдослучайных чисел;

  • ограниченное время жизни (одна сессия);

  • уникальность для каждой транзакции;

  • устойчивый к подбору размер (в битах);

  • невозможно переиспользовать.

Типы токенов

Существует три основных типа токенов по способу генерации:

  1. Synchronizer Tokens или Anti-CSRF (токены синхронизации). В этом случае инициатором ключа выступает сервер — на нём хранится исходная шифровка. Когда браузер обращается к серверу и предъявляет ему ключ, сервер сравнивает его с исходником и в зависимости от результата продолжает или прерывает сессию.
  2. Double Submit Cookie (двойная отправка куки). При этом способе токен нигде не хранится. Когда браузер обращается к серверу впервые за сессию, сервер генерирует и передаёт ему ключ в двух формах: через куки и в одном из параметров ответа. При следующих обращениях браузера сервер дважды проверяет правильность ключа — в параметрах и в куках.
  3. Encrypted Token (зашифрованный токен). Этот способ предполагает, что ключом шифруется какая-то часть информации о клиенте, которая содержится в браузере. При первом запросе браузера сервер получает информацию о пользователе, зашифровывает её и передаёт браузеру токен. При следующем взаимодействии сервер расшифровывает токен и сверяет информацию.

Помимо токенов, для защиты используется флаг Same-Site (большинство браузеров его поддерживает). Он работает напрямую для cookies и позволяет помечать куки конкретного домена. Сервер проверяет, содержатся ли нужные пометки в куках страницы, с которых происходит оплата или вносятся изменения. Если пометок нет — сессия прекращается.

Также в качестве меры защиты на страницах сайта настраивают форму с капчей. Это особенно актуально для страниц смены пароля или совершения денежных транзакций.

«Истек срок действия токена» или «CSRF-значение недопустимо»: что это значит и что делать

Даже при авторизации на сайтах, для которых настроена защита от атак, можно встретить следующие варианты сообщения об ошибке: «Недопустимое CSRF-значение»/«CSRF-токены не совпадают» или «Token expired» (в переводе — срок действия токена истек). Сообщение может отображаться как на английском, так и на русском. Пример ошибки при авторизации на сайте REG.RU:



Ошибка токен истек 2

Обычно ошибка возникает по двум основным причинам:

  • сервер некорректно сгенерировал токен;

  • срок токена истек — пользователь долго не совершал никаких действий на странице.

В обоих случаях исправить проблему поможет перезагрузка страницы — вы запустите новую сессию, а значит, сервер и браузер договорятся о новом рабочем токене. Для этого нажмите на значок обновления страницы:


Ошибка токен истек 3

Иногда ошибка возникает из-за расширений защиты конфиденциальности или плагинов блокировки рекламы (например, Ghostery, UBlock Origin, Blur), которые настроены у пользователя. В этом случае можно отключить расширение. Также можно добавить сайт, на котором появилось сообщение, в список доверенных сайтов.

На примере сайта reg.ru покажем, что для этого нужно:

в Google Chrome

  1. Откройте настройки Chrome:

    Ошибка токен истек 4

  2. В списке слева выберите Конфиденциальность и безопасность, а затем Файлы cookie и другие данные сайтов.
  3. Внизу страницы откройте Сайты, которые всегда могут использовать файлы cookie и кликните Добавить.
  4. Введите «[*.]www.reg.ru» и нажмите Добавить.
  5. Нажмите Все файлы cookie и данные сайта и удалите все записи, которые связаны с сайтом reg.ru.
  6. Перезагрузите браузер и выполните операцию повторно.

в Яндекс.Браузер

  1. Откройте настройки браузера Яндекс:

    20220125_chto_oznachayet_oshibka_csrf_token_istek_5.png

  2. Перейдите на Сайты Расширенные.
  3. Кликните Настройки… для первого параметра в списке. Затем на вкладке «Разрешена» введите www.reg.ru и кликните Добавить.
  4. Добавьте адрес сайта для всех параметров списка по аналогии.

в Safari

  1. Откройте настройки Safari комбинацией Cmd + , (⌘,).
  2. Перейдите на вкладку Конфиденциальность и проверьте, что в пункте «Файлы cookie и данные веб-сайтов» не выбрано «Блокировать все файлы cookie». Если это так, снимите настройки.
  3. Кликните Управление данными веб-сайтов и удалите все записи, которые относятся к www.reg.ru.
  4. Перезагрузите браузер и выполните операцию повторно.

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

Заключение

Успешная атака CSRF позволяет хакеру действовать на сайте от имени другого зарегистрированного посетителя. Чтобы мошенник не добрался до конфиденциальных данных, для сайта нужно настроить один из типов CSRF-токенов. Токены позволяют серверу и браузеру безопасно обмениваться информацией в течение сессии. Однако даже на безопасных сайтах можно столкнуться с ошибкой «токен CSRF истек». В этом нет ничего страшного. Чтобы возобновить подключение, достаточно обновить страницу браузера.

Я получаю это сообщение об ошибке каждый раз, когда я пытаюсь отправить форму:

Символ CSRF недействителен. Повторите отправку формы

Мой код формы:

<form novalidate action="{{path('signup_index')}}" method="post" {{form_enctype(form)}} role="form" class="form-horizontal">
    <div class="form-group">
        {{ form_label(form.email, 'Email', {'label_attr': {'class': 'col-md-1 control-label'}}) }}
        {{ form_widget(form.email, {'attr': {'class': 'col-md-2'}}) }}
        {{ form_errors(form.email) }}
    </div>

    <div class="form-group">
        {{ form_label(form.nickname, 'Nickname', {'label_attr': {'class': 'col-md-1 control-label'}}) }}
        {{ form_widget(form.nickname, {'attr':{'class': 'col-md-2'}}) }}
        {{ form_errors(form.nickname, {'attr': {'class': 'col-md-3'}}) }}
    </div>
    <div class="form-group">
        {{ form_label(form.password, 'password', {'label_attr': {'class': 'col-md-1 control-label'}}) }}
        {{ form_widget(form.password, {'attr': {'class': 'col-md-2'}}) }}
        {{ form_errors(form.password, {'attr': {'class': 'col-md-3'}}) }}
    </div>

    <div class="form-group">
        {{ form_label(form.password_repeat, 'Repeat password', {'label_attr': {'class': 'col-md-1 control-label'}}) }}
        {{ form_widget(form.password_repeat, {'attr':{'class': 'col-md-2'}}) }}
        {{ form_errors(form.password_repeat, {'attr': {'class': 'col-md-3'}}) }}
    </div>
    <div class="form-group">
        <div class="col-md-1 control-label">
        <input type="submit" value="submit">
    </div>

    </div>
</form>

Любые идеи?

Ответ 1

Вам нужно добавить _token в вашу форму i.e

{{ form_row(form._token) }}

На данный момент в вашей форме отсутствует поле токена CSRF. Если вы используете функции формы твига для рендеринга вашей формы, например form(form), это автоматически отобразит поле маркера CSRF для вас, но ваш код показывает, что вы создаете форму с необработанным HTML, например <form></form>, поэтому вам нужно вручную отобразить поле.

Или просто добавьте {{ form_rest(form) }} перед закрывающим тегом формы.

Согласно документам

Это отображает все поля, которые еще не были отображены для данного форма. Это хорошая идея всегда иметь это где-то внутри вашей формы так как это сделает скрытые поля для вас и сделает все поля, которые вы забыли чтобы сделать более очевидным (поскольку он отобразит поле для вас).

form_rest (просмотр, переменные)

Ответ 2

Также вы можете увидеть это сообщение об ошибке, если в вашей форме много элементов.

Эта опция в php.ini вызывает проблему

; How many GET/POST/COOKIE input variables may be accepted
 max_input_vars = 1000

Проблема в том, что поле _token пропускает запрос PUT (GET), поэтому вам нужно увеличить значение.

Также это касается больших файлов. Увеличение

upload_max_filesize

Опция

решит проблему.

Ответ 3

Это происходит потому, что формы по умолчанию содержат защиту CSRF, которая в некоторых случаях не нужна.

Вы можете отключить эту защиту CSRF в своем классе формы в методе getDefaultOptions следующим образом:

// Other methods omitted

public function getDefaultOptions(array $options)
{
    return array(
        'csrf_protection' => false,
        // Rest of options omitted
    );
}

Если вы не хотите отключать защиту CSRF, вам необходимо отобразить поле защиты CSRF в вашей форме. Это можно сделать, используя {{ form_rest(form) }} в вашем файле вида, например:

<form novalidate action="{{path('signup_index')}}" method="post" {{form_enctype(form)}} role="form" class="form-horizontal">
    <!-- Code omitted -->

    <div class="form-group">
        <div class="col-md-1 control-label">
            <input type="submit" value="submit">
        </div>

    </div>
    {{ form_rest(form) }}
</form>

{{ form_rest(form) }} отображает все поля, которые вы не ввели вручную.

Ответ 4

Перед тегом </form> поставьте:

{{ form_rest(form) }}

Он автоматически вставляет другие важные (скрытые) входы.

Ответ 5

В дополнение к предложениям других вы можете получить ошибки токена CSRF, если ваша память сеанса не работает.

В недавнем случае мой коллега изменил «session_prefix» на значение, в котором было пробел.

session_prefix: 'My Website'

Это сломало хранилище сеансов, что, в свою очередь, означало, что моя форма не могла получить токен CSRF из сеанса.

Ответ 6

У меня была эта проблема со странным поведением: очистка кеша браузера не исправила его, но очистка файлов cookie (то есть файлов cookie сеанса PHP) решила проблему.

Это нужно сделать после того, как вы проверили все другие ответы, включая проверку того, что у вас есть токен в поле ввода скрытой формы.

Ответ 7

У меня была эта ошибка в последнее время. Оказывается, мои настройки cookie были неправильными в config.yml. Добавление настроек cookie_path и cookie_domain в framework.session исправлено.

Ответ 8

Недавно я столкнулся с той же проблемой, и мой случай был чем-то, о чем здесь еще не говорилось:

Проблема была в том, что я тестировал его в домене localhost. Я не уверен, почему именно это было проблемой, но она начала работать после того, как я добавил псевдоним имени хоста для localhost в /etc/hosts следующим образом:

127.0.0.1        foobar

Возможно, что-то не так с сеансом при использовании Apache и localhost в качестве домена. Если кто-то может уточнить комментарии, я был бы рад отредактировать этот ответ, чтобы включить больше деталей.

Ответ 9

Если вы не хотите использовать form_row или form_rest и хотите получить доступ к значению _token в шаблоне ветки. Используйте следующее:

<input type="hidden" name="form[_token]" value="{{ form._token.vars.value }}" />

Ответ 10

В моем случае у меня возникла проблема с аннотацией maxSize в сущности, поэтому я увеличил ее с 2048 по 2004 год.

 /**
 * @AssertFile(
 *     maxSize = "20048k",
 *     mimeTypes = {"application/pdf", "application/x-pdf"},
 *     mimeTypesMessage = "Please upload a valid PDF"
 * )
 */
private $file;

надеюсь, что этот ответ поможет!

Ответ 11

Если вы преобразовали форму из простого HTML в веточку, убедитесь, что вы не пропустили удаление закрывающего тега </form>. Глупая ошибка, но поскольку я обнаружил это возможную причину этой проблемы.

Когда я получил эту ошибку, я не мог понять это сначала. Я использую form_start() и form_end() для создания формы, поэтому мне не нужно явно добавлять токен с помощью form_row(form._token) или использовать form_rest() для его получения. Он должен быть добавлен автоматически form_end().

Проблема заключалась в том, что представление, с которым я работал, было тем, что я преобразовал из простого HTML в веточку, и я пропустил удаление закрывающего тега </form>, поэтому вместо:

{{ form_end(form) }}

У меня было:

</form>
{{ form_end(form) }}

Это похоже на то, что может вызвать ошибку, но, по-видимому, это не так, поэтому, когда form_end() выводит form_rest(), форма уже закрыта. Фактически созданный источник страницы формы был таким:

<form>
    <!-- all my form fields... -->
</form>
<input type="hidden" id="item__token" name="item[_token]" value="SQAOs1xIAL8REI0evGMjOsatLbo6uDzqBjVFfyD0PE4" />
</form>

Очевидно, что решение состоит в том, чтобы удалить дополнительный закрывающий тег и, возможно, выпить еще немного кофе.

Ответ 12

Я столкнулся с подобной проблемой. Убедившись, что поле токена действительно отображено (см. Принятый ответ), я проверил свои куки. В моем браузере Chrome было 2 (!) Файла cookie для этого домена, по-видимому, потому что я запускал приложение в том же домене, что и другое приложение, но с другим портом (т.е. Mydomain.com установил исходный файл cookie во время работы приложения с ошибками). на mydomain.com:123) Теперь, видимо, Chrome отправил неправильный файл cookie, поэтому защита CSRF не смогла связать токен с правильным сеансом.

Исправление: очистите все куки для данного домена, убедитесь, что вы не запускаете несколько приложений в одном домене с разными портами.

Ответ 13

У меня была та же ошибка, но в моем случае проблема заключалась в том, что мое приложение использовало несколько доменов первого уровня, в то время как cookie использовал один. Удаление cookie_domain: ".%domain%" из framework.session в config.yml приводило к тому, что cookie по умолчанию использовался для любого домена, на котором была форма, и это config.yml проблему.

Ответ 14

Это кажется проблемой при использовании bootstrap, если вы не передаете форму {{form (form)}}. Кроме того, проблемы возникают только при вводе типа = «скрытый». Если вы проверите страницу с помощью формы, вы обнаружите, что скрытый ввод не является частью разметки вообще или визуализируется, но не представляется по какой-либо причине. Как было предложено выше, добавление {{form_rest (form)}} или перенос ввода, как показано ниже, должен сделать трюк.

<div class="form-group">
    <input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">
</div>

Уже **** горит жестко от этой ошибки. Уже несколько дней работаю с этой csurf, всё отлично, всё окей, но начиная с какого-то момента перестал работать на странице продукта (AdminRoutes)
На сайте есть аутентификация, есть формы по типу добавки товара в корзину через форму с csrf токеном и еще несколько таких форм с csrf, везде работает корректно, но именно при добавлении и изменении продукта выдает ошибку Invalid csrf token.
Голова уже не может думать, просто загадка года для меня…
Заранее очень благодарю за помощь.

app.js

const path = require('path')
const bodyParser = require('body-parser');
const session = require('express-session')
const MongoDBSession = require('connect-mongodb-session')(session)
const csrf = require('csurf')
const flash = require('connect-flash')

const express = require('express');
const mongoose = require('mongoose')
const app = express();

const MONGODB_URI = 'mongodb+srv://login:password@mongooset.ekaxg.mongodb.net/shop?retryWrites=true&w=majority'
const sessionStore = new MongoDBSession({
    uri: MONGODB_URI,
    collection: 'session'
})

app.set('view engine', 'ejs');
app.set('views', 'views');

const adminRoutes = require('./routes/AdminRoutes');
const shopRoutes = require('./routes/ShopRoutes');
const cartRoutes = require('./routes/CartRoutes');
const orderRoutes = require('./routes/OrderRoutes');
const authRoutes = require('./routes/AuthRoutes');
const User = require('./models/UserModel');
const errorController = require('./controllers/ErrorController')

app.use(bodyParser.urlencoded({extended: true}));
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
    secret: 'some secret key',
    resave: false,
    saveUninitialized: false,
    store: sessionStore,
}));

const csrfProtection = csrf()
app.use(flash())
app.use(csrfProtection)

app.use((req, res, next) => {
    res.locals.isAuthenticated = req.session.isAuthenticated
    res.locals.csrf = req.csrfToken()
    next()
})

app.use((req, res, next) => {
    if (!req.session.isAuthenticated) {
        return next()
    }
    User.find({ email: req.session.email }, (err, user) => {
        if (err) console.log(err)
        if (user.length > 0) {
            req.user = user[0]
        } else {
            req.user = []
        }
        return next()
    })
})

app.use('/admin', adminRoutes);
app.use(shopRoutes);
app.use(cartRoutes);
app.use(orderRoutes);
app.use(authRoutes);

app.use(errorController.get404);

app.use((error, req, res, next) => {
    console.log(error);
    res.status(500).render('500', {
        pageTitle: 'Something went wrong',
        page: '500',
    })
})

mongoose.connect(MONGODB_URI)
    .then(() => {
        console.log('Connected to Mongoose')
        app.listen(8000)
    })
    .catch(err => {
        console.log(err)
    })

AdminRoutes.js

const express = require('express');
const router = express.Router();

const adminController = require('../controllers/AdminController')
const isAuth = require('../middleware/is-auth')

// GET
router.get('/products', isAuth, adminController.getProducts);
router.get('/add-product', isAuth, adminController.getAddProduct);
router.get('/edit-product/:id', isAuth, adminController.getEditProduct);

// POST
router.post('/add-product', isAuth, adminController.postAddProduct);
router.post('/edit-product/:id', isAuth, adminController.postEditProduct);
router.post('/delete-product', isAuth, adminController.deleteProduct);

module.exports = router
<input type="hidden" name="_csrf" value="<%= csrf %>">

csrf токен во всех шаблонах присутствует, проверил уже. Везде одинакого и везде работает, ну кроме этого роута.

Открытые члены

  __construct (bool $enabled=true, string $tokenName=’sessid’, bool $returnNew=true)
 
  listAllowedScopes ()
 
  onBeforeAction (Event $event)
 
  bindAction (Action $action)
 
  getAction ()
 
  onAfterAction (Event $event)
 
  getErrors ()
 
  getErrorByCode ($code)
 

Открытые статические члены

static  className ()
 

См. определение в файле csrf.php строка 13

◆ __construct()

__construct ( bool  $enabled = true,
string  $tokenName = 'sessid',
bool  $returnNew = true 
)

Csrf constructor.

Аргументы
bool $enabled
string $tokenName
bool $returnNew

См. определение в файле csrf.php строка 38

39 {

40 $this->enabled = $enabled;

41 $this->tokenName = $tokenName;

42 $this->returnNew = $returnNew;

43 parent::__construct();

44 }

◆ addError()

addError ( Error  $error )
protectedinherited

Adds error to error collection.

Аргументы
Возвращает
$this

См. определение в файле base.php строка 80

81 {

82 $this->errorCollection[] = $error;

83

84 return $this;

85 }

◆ bindAction()

bindAction ( Action  $action )
finalinherited

См. определение в файле base.php строка 38

39 {

41

42 return $this;

43 }

◆ className()

static className ( )
staticfinalinherited

Returns the fully qualified name of this class.

Возвращает
string

См. определение в файле base.php строка 25

26 {

27 return get_called_class();

28 }

◆ getAction()

getAction ( )
finalinherited

◆ getErrorByCode()

getErrorByCode (   $code )
finalinherited

Getting once error with the necessary code.

Аргументы
string $code Code of error.
Возвращает
Error

Замещает Errorable.

См. определение в файле base.php строка 101

102 {

103 return $this->errorCollection->getErrorByCode($code);

104 }

◆ getErrors()

getErrors ( )
finalinherited

Getting array of errors.

Возвращает
Error[]

Замещает Errorable.

См. определение в файле base.php строка 91

92 {

93 return $this->errorCollection->toArray();

94 }

◆ listAllowedScopes()

List allowed values of scopes where the filter should work.

Возвращает
array

Переопределяет метод предка Base.

См. определение в файле csrf.php строка 50

51 {

52 return [

54 ];

55 }

◆ onAfterAction()

onAfterAction ( Event  $event )
inherited

◆ onBeforeAction()

onBeforeAction ( Event  $event )

Переопределяет метод предка Base.

См. определение в файле csrf.php строка 57

58 {

59 if (!$this->enabled)

60 {

61 return null;

62 }

63

64 if (!check_bitrix_sessid($this->tokenName))

65 {

66 $errorCustomData = [];

67 if ($this->returnNew)

68 {

69 $errorCustomData[‘csrf’] = bitrix_sessid();

71 self::HEADER_WITH_NEW_CSRF, $errorCustomData[‘csrf’]

72 );

73 }

74

76 ‘Invalid csrf token’,

77 self::ERROR_INVALID_CSRF, $errorCustomData

78 ));

79

80 return new EventResult(EventResult::ERROR, null, null, $this);

81 }

82

83 return null;

84 }

◆ $action

◆ $errorCollection

◆ ERROR_INVALID_CSRF

const ERROR_INVALID_CSRF = ‘invalid_csrf’

См. определение в файле csrf.php строка 16

◆ HEADER_WITH_NEW_CSRF

const HEADER_WITH_NEW_CSRF = ‘X-Bitrix-New-Csrf’

См. определение в файле csrf.php строка 15


Объявления и описания членов класса находятся в файле:

  • C:/Bitrix/modules/main/lib/engine/actionfilter/csrf.php

CSRF Protection Problem and How to Fix it

One day I was working on a feature at work. I had many branches created in JIRA tickets, so I wanted to open a bunch of PRs (Pull Requests) all at once in different tabs.

This is how I usually work – I have a lot of tabs open and this speeds things up, because I don’t need to wait for the next page to load.

But after I’d created the first PR in BitBucket and tried to go on to the next page, I was welcomed with an error message about an invalid CSRF token. This is a common problem with web applications that have CSRF protection.

So in this article you’ll learn what CSRF is and how to fix this error.

Table of contents:

  • What is CSRF?
  • Standard CSRF protection
  • The Problem with Tokens
  • Cross-tab Communication Solution
    • Sysend library
    • Broadcast Channel
  • Conclusion

What is CSRF?

CSRF is an acronym for Cross-Site Request Forgery. It is a vector of attack that attackers commonly use to get into your system.

The way you usually protect against CSRF is to send a unique token generated by each HTTP request. If the token that is on the server doesn’t match with the one from the request, you show an error to the user.

Standard CSRF protection

This is one way you can protect against CSRF with a token:

const inital_token = '...';

const secure_fetch = (token => {
    const CSRF_HEADER = 'X-CSRF-TOKEN';
    return (url) => {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
              [CSRF_HEADER]: token
            }
        });
        response.then(res => {
           token = res.headers[CSRF_HEADER]
        });
        return response;
    };
})(inital_token);

This code uses the fetch API to send and receive a secure token in HTTP headers. On the backed, you should generate the first initial token when the page loads. On the server, on each AJAX request, you should check to see if the token is valid.

The Problem with Tokens

This works fine unless you have more than one tab open. Each tab can send requests to the server, which will break this solution. And power users may not be able to use your application the way they want.

But there is a simple solution to this problem which is cross-tab communication.

Cross-tab Communication Solution

Sysend library

You can use the Sysend library, an open source solution that I’ve created specifically for this purpose. It simplifies cross-tabs communication.

If you want, you can use a native browser API like Broadcast Channel to do the same. More on how to do that later in this article.

But the Sysend library will work for browsers that don’t support Broadcast Channel. It also works in IE (it has some bugs, which is not a surprise). You may also need to support some old mobile browsers. It also has a much simpler API.

This is the simplest example:

let token;
sysend.on('token', new_token => {
    token = new_token;
});

// ...

sysend.broadcast('token', token);
Simple example of using base function of sysend library

And this is how you would use this library to fix CSRF protection:

const inital_token = '...';

const secure_fetch = (token => {
    const CSRF_HEADER = 'X-CSRF-TOKEN';
    const EVENT_NAME = 'csrf';
    sysend.on(EVENT_NAME, new_token => {
        // get new toke from different tab
        token = new_token;
    });
    return (url) => {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
              [CSRF_HEADER]: token
            }
        });
        response.then(res => {
           token = res.headers[CSRF_HEADER];
           // send new toke to other tabs
           sysend.broadcast(EVENT_NAME, token); 
        });
        return response;
    };
})(inital_token);
secure_fetch function with CSRF protection using sysend

All you have to do is to send and receive a single message from other tabs when sending the request. And your CSRF protected app will work on many tabs.

And that’s it. This will let advanced users use your app that has CSRF protection when they want to open many tabs.

Broadcast Channel

Here is the simplest possible example of using Broadcast Channel:

const channel = new BroadcastChannel('my-connection');
channel.addEventListener('message', (e) => {
    console.log(e.data); // 'some message'
});
channel.postMessage('some message');
Basic usage of Broadcast Channel

So with this simple API you can do the same thing that we did before:

const inital_token = '...';

const secure_fetch = (token => {
    const CSRF_HEADER = 'X-CSRF-TOKEN';
    const channel = new BroadcastChannel('csrf-protection');
    channel.addEventListener('message', (e) => {
        // get new toke from different tab
    	token = e.data;
    });
    return (url) => {
        const response = await fetch(url, {
            method: 'POST',
            headers: {
              [CSRF_HEADER]: token
            }
        });
        response.then(res => {
           token = res.headers[CSRF_HEADER];
           // send new token to other tabs
           channel.postMessage(token);
        });
        return response;
    };
})(inital_token);
secure_fetch function with CSRF protection using BroadcastChannel

As you can see from the above example, Broadcast Channel doesn’t have any namespace for events. So if you want to send more than one type of event you need to create types of events.

Here is an example of using Broadcast Channel to do more than the CSRF protection fix we’ve discussed so far.

You can synchronize login and logout for your application. If you login into one tab, your other tabs will also sign you in. In the same way, you can synchronize the shopping cart in some e-commerce websites.

const channel = new BroadcastChannel('my-connection');
const CSRF = 'app/csrf';
const LOGIN = 'app/login';
const LOGOUT = 'app/logout';
let token;
channel.addEventListener('message', (e) => {
    switch (e.data.type) {
        case CSRF:
            token = e.data.payload;
            break;
        case LOGIN:
            const { user } = e.data.payload;
            autologin(user);
            break;
        case LOGOUT:
            logout();
            break;
    }
});

channel.postMessage({type: 'login', payload: { user } });
Using Broadcast Channel with different type of messages

Conclusion

It’s great if you protect your app against attackers. But keep in mind how people will be using your application, too so you don’t make it unnecessarily hard to use. This applies not only to this particular problem.

The Sysend library is a simple way to communicate between open tabs in the same browser. And it can fix major issues with CSRF protection. The library has more features, and you can check its GitHub repo for more details.

Broadcast Channel is also not that complicated. If you don’t need to support old browsers or some older mobile devices, you can use this API. But if you need to support older browsers, or want to make your code simpler, you use can the sysend library.

If you want to see browser support for Broadcast Channel, you can see Can I Use.



Learn to code for free. freeCodeCamp’s open source curriculum has helped more than 40,000 people get jobs as developers. Get started

У меня на странице есть форма с неколькими полями и кнопкой. При нажатии на кнопку данные должны отправляться на сервер и там обрабатываться. Использую стандартные asp контролы: asp:TextBox и asp:Button. В серверной части обрабатываю событие OnClick у кнопки.
Если страница не кеширована, то все нормально. Если же нет, то возникает ошибка Provided security token is invalid!

Provided security token is invalid!
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

Exception Details: Bitrix.Security.BXCsrfTokenValidationException: Provided security token is invalid!

Source Error:

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

Stack Trace:

[BXCsrfTokenValidationException: Provided security token is invalid!]
  Bitrix.Security.BXCsrfToken.ValidateToken(String token) +92
  Bitrix.Security.BXCsrfToken.ValidatePost() +227
  Bitrix.UI.TemplateRequisite.PageInitComplete(Object sender, EventArgs e) +53
  System.EventHandler.Invoke(Object sender, EventArgs e) +0
  System.Web.UI.Page.OnInitComplete(EventArgs e) +11032014
  Bitrix.UI.BXPublicPage.OnInitComplete(EventArgs e) +16
  System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1674

Кто знает как побороть это на кешированных страницах?
Кеширование настроено так:
<%@ OutputCache Duration=»420″ Location=»ServerAndClient» VaryByParam=»*» %>

Если вы столкнулись с ошибкой «истек CSRF-токен» — читайте нашу статью. Из неё вы узнаете, как работает CSRF-token защита, и что делать, если CSRF токен истек.


Ошибка токен истек 1

Что такое CSRF

CSRF (англ. cross-site request forgery) — это межсайтовая подделка запроса. Это атака, которой может подвергаться любой веб-ресурс или веб-приложение. В первую очередь это касается сайтов, которые используют cookies, сертификаты авторизации и браузерную аутентификацию. В результате атаки страдают клиенты и репутация ресурса.

Вредоносный скрипт прячется в коде сайта или обычной ссылке. С помощью него мошенник получает доступ к конфиденциальной информации: платежным реквизитам, логину и паролю, личной переписке. После того как данные “в кармане”, хакер может изменить пароль, указать свой номер телефона или email, перевести деньги на свой счёт и многое другое.

Как работает CSRF-атака

Злоумышленник может использовать фишинговую ссылку — это наиболее распространенный способ обмана. В этом случае атака работает по следующей схеме:

  1. Злоумышленник создаёт поддельную страницу, очень похожую на оригинальную, и встраивает её в сайт. В коде ссылка может выглядеть так: <a href=“вредоносная ссылка”>Unsubscribe here</a>.
  2. Пользователь переходит с одной страницы сайта на другую (например, на страницу оплаты) и вместо реальной страницы попадает на поддельную.
  3. Пользователь совершает действие на странице, например, оплачивает товар или вводит данные авторизации.
  4. Информация или денежные средства вместо оригинального сервера уходят на сервер мошенника.

CSRF-атаки случаются из-за того, что без специальных настроек сервер не может с точностью в 100% определить, кто именно выполняет действия со стороны пользователя. Он не может проверить, действительно ли на кнопку “оплатить” нажал тот пользователь, который изначально открыл страницу с оплатой. Хакеры активно используют этот люфт в безопасности HTTP-запросов и применяют вредоносные скрипты. Однако от атаки можно защититься с помощью CSRF-токенов. 

Что такое CSRF-token и как он работает

В общем понимании токен — это механизм, который позволяет идентифицировать пользователя или конкретную сессию для безопасного обмена информацией и доступа к информационным ресурсам. Токены помогают проверить личность пользователя (например, клиента, который онлайн получает доступ к банковскому счёту). Их используют как вместо пароля, так и вместе с ним. Токен — это в каком-то смысле электронный ключ.

CSRF-token — это максимально простой и результативный способ защиты сайта от CSRF-мошенников. Он работает так: сервер создаёт случайный ключ (он же токен) и отправляет его браузеру клиента. Когда браузер запрашивает у сервера информацию, сервер, прежде чем дать ответ, требует показать ключ и проверяет его достоверность. Если токен совпадает, сессия продолжается, а если нет — прерывается. Токен действителен только одну сессию — с новой сессией он обновляется.

Чтобы получить ответ от сервера, используются разные методы запроса. Условно они делятся на две категории: те, которые не изменяют состояние сервера (GET, TRACE, HEAD), и те, которые изменяют (PUT, PATCH, POST и DELETE). Последние имеют большую CSRF-уязвимость и поэтому должны быть защищены в первую очередь.

При создании и использовании токена должны соблюдаться следующие условия:

  • нахождение в скрытом параметре;

  • генерация с помощью генератора псевдослучайных чисел;

  • ограниченное время жизни (одна сессия);

  • уникальность для каждой транзакции;

  • устойчивый к подбору размер (в битах);

  • невозможно переиспользовать.

Типы токенов

Существует три основных типа токенов по способу генерации:

  1. Synchronizer Tokens или Anti-CSRF (токены синхронизации). В этом случае инициатором ключа выступает сервер — на нём хранится исходная шифровка. Когда браузер обращается к серверу и предъявляет ему ключ, сервер сравнивает его с исходником и в зависимости от результата продолжает или прерывает сессию.
  2. Double Submit Cookie (двойная отправка куки). При этом способе токен нигде не хранится. Когда браузер обращается к серверу впервые за сессию, сервер генерирует и передаёт ему ключ в двух формах: через куки и в одном из параметров ответа. При следующих обращениях браузера сервер дважды проверяет правильность ключа — в параметрах и в куках.
  3. Encrypted Token (зашифрованный токен). Этот способ предполагает, что ключом шифруется какая-то часть информации о клиенте, которая содержится в браузере. При первом запросе браузера сервер получает информацию о пользователе, зашифровывает её и передаёт браузеру токен. При следующем взаимодействии сервер расшифровывает токен и сверяет информацию.

Помимо токенов, для защиты используется флаг Same-Site (большинство браузеров его поддерживает). Он работает напрямую для cookies и позволяет помечать куки конкретного домена. Сервер проверяет, содержатся ли нужные пометки в куках страницы, с которых происходит оплата или вносятся изменения. Если пометок нет — сессия прекращается.

Также в качестве меры защиты на страницах сайта настраивают форму с капчей. Это особенно актуально для страниц смены пароля или совершения денежных транзакций.

«Истек срок действия токена» или «CSRF-значение недопустимо»: что это значит и что делать

Даже при авторизации на сайтах, для которых настроена защита от атак, можно встретить следующие варианты сообщения об ошибке: «Недопустимое CSRF-значение»/«CSRF-токены не совпадают» или «Token expired» (в переводе — срок действия токена истек). Сообщение может отображаться как на английском, так и на русском. Пример ошибки при авторизации на сайте REG.RU:



Ошибка токен истек 2

Обычно ошибка возникает по двум основным причинам:

  • сервер некорректно сгенерировал токен;

  • срок токена истек — пользователь долго не совершал никаких действий на странице.

В обоих случаях исправить проблему поможет перезагрузка страницы — вы запустите новую сессию, а значит, сервер и браузер договорятся о новом рабочем токене. Для этого нажмите на значок обновления страницы:


Ошибка токен истек 3

Иногда ошибка возникает из-за расширений защиты конфиденциальности или плагинов блокировки рекламы (например, Ghostery, UBlock Origin, Blur), которые настроены у пользователя. В этом случае можно отключить расширение. Также можно добавить сайт, на котором появилось сообщение, в список доверенных сайтов.

На примере сайта reg.ru покажем, что для этого нужно:

в Google Chrome

  1. Откройте настройки Chrome:

    Ошибка токен истек 4

  2. В списке слева выберите Конфиденциальность и безопасность, а затем Файлы cookie и другие данные сайтов.
  3. Внизу страницы откройте Сайты, которые всегда могут использовать файлы cookie и кликните Добавить.
  4. Введите «[*.]www.reg.ru» и нажмите Добавить.
  5. Нажмите Все файлы cookie и данные сайта и удалите все записи, которые связаны с сайтом reg.ru.
  6. Перезагрузите браузер и выполните операцию повторно.

в Яндекс.Браузер

  1. Откройте настройки браузера Яндекс:

    20220125_chto_oznachayet_oshibka_csrf_token_istek_5.png

  2. Перейдите на Сайты Расширенные.
  3. Кликните Настройки… для первого параметра в списке. Затем на вкладке «Разрешена» введите www.reg.ru и кликните Добавить.
  4. Добавьте адрес сайта для всех параметров списка по аналогии.

в Safari

  1. Откройте настройки Safari комбинацией Cmd + , (⌘,).
  2. Перейдите на вкладку Конфиденциальность и проверьте, что в пункте «Файлы cookie и данные веб-сайтов» не выбрано «Блокировать все файлы cookie». Если это так, снимите настройки.
  3. Кликните Управление данными веб-сайтов и удалите все записи, которые относятся к www.reg.ru.
  4. Перезагрузите браузер и выполните операцию повторно.

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

Заключение

Успешная атака CSRF позволяет хакеру действовать на сайте от имени другого зарегистрированного посетителя. Чтобы мошенник не добрался до конфиденциальных данных, для сайта нужно настроить один из типов CSRF-токенов. Токены позволяют серверу и браузеру безопасно обмениваться информацией в течение сессии. Однако даже на безопасных сайтах можно столкнуться с ошибкой «токен CSRF истек». В этом нет ничего страшного. Чтобы возобновить подключение, достаточно обновить страницу браузера.

Битрикс24 - создание контроллера

Задача создать свой собственный REST используя контроллеры в Битрикс.

Создам ресурс GET bitrix-domain.local/almat/su/user/, который будет возвращать все ФИО всех пользователей.

Также туда можно будет передать фильтрь params, по которому можно будет отфильтровать пользователей. Для создания своих контроллеров понадобится сначала создать каркас собственного модуля. Как это сделать описано здесь:

Создание собственного модуля в Битрикс

По теории о контроллерах в Битрикс можно почитать тут

https://dev.1c-bitrix.ru/learning/course/index.php?LESSON_ID=6436&COURSE_ID=43

Также, чтобы получить красивые урлы нужно будет поменять настройки роутинга.

1 . Изменить в .htaccess, который лежит в корне

#RewriteCond %{REQUEST_FILENAME} !/bitrix/urlrewrite.php$

#RewriteRule ^(.*)$ /bitrix/urlrewrite.php [L]

RewriteCond %{REQUEST_FILENAME} !/bitrix/routing_index.php$

RewriteRule ^(.*)$ /bitrix/routing_index.php [L]

2. Изменить в bitrix/.settings.php

‘routing’ => [‘value’ => [

  ‘config’ => [‘web.php’, ‘api.php’]

]],

3. Добавить в папку local файл

/local/routes/api.php

Весь код можно увидеть по ссылке пример контроллера в Битрикс

Скачайте его на свой сервер, обратите внимание на папку controller, где создан сам контроллер. Также обратите внимание на папку include, где происходит добавление нового пространства имен AlmatSuController

Теперь при отправке по адресу

curl location ‘http://192.168.1.10/almat/su/user/’ , где 192.168.1.10 это домен Битрикса

мы видим… требование авторизации. По умолчанию, битрикс требует авторизацию для кастомных роутов.

Авторизуемся по Basic Auth. Новый запрос выглядет теперь так:

curl location ‘http://192.168.1.10/almat/su/user/’ 

header ‘Authorization: Basic YWRtaW46MTIzNDU2

Этот пример можно импортировать в Postman

Логин и пароль вводите от существуюшего пользователя.

получите ошибку

{«status»:«error»,«data»:null,«errors»:[{«message»:«Invalid csrf token»,«code»:«invalid_csrf»,«customData»:{«csrf»:«9e8b89552254d13c759cd1b0a55920aa»}}]}

Тут есть два выхода

  1. Передать csrf в параметре sessid

curl location ‘http://192.168.1.10/almat/su/user/?sessid=9e8b89552254d13c759cd1b0a55920aa’

header ‘Authorization: Basic YWRtaW46MTIzNDU2’

2. Отключить проверку csrf

переопределив метод getDefaultPreFilters, удалив проверку Csrf

public function getDefaultPreFilters(): array

{

    return [

        new ActionFilterAuthentication(),

        new ActionFilterHttpMethod(

            [ActionFilterHttpMethod::METHOD_GET, ActionFilterHttpMethod::METHOD_POST]

        ),

    ];

}

Теперь при обращении по этому роуту возвращаются данные:

пример контроллера в Битрикс24

пример контроллера в Битрикс

Как фильтровать данные?

Для фильтрации данных нужно передать params c нужным фильтром, например, выбор пользователя с ID = 1 будет выглядеть так

curl location globoff ‘http://192.168.1.10/almat/su/user/?params[ID]=1’

header ‘Authorization: Basic YWRtaW46MTIzNDU2’

который вернет

{

    «status»: «success»,

    «data»: {

        «1»: «Алмат Жандаулетов»

    },

    «errors»: []

}

  • Ошибка invalid conversion from const char to char fpermissive
  • Ошибка invalid client вконтакте на телефоне
  • Ошибка invalid argument to date encode
  • Ошибка internet explorer обнаружена ошибка
  • Ошибка internal provider error market csgo