Як реалізувати CSRF-захист

Дата оновлення перекладу 2024-05-30

Як реалізувати CSRF-захист

CSRF - або Міжсайтова підробка запиту - це метод, яким зловмисний користувач намагається змусити ваших користувачів, не знаючи про це, відправляти дані, які вони не збиралися відправляти.

Захист від CSRF працює шляхом додавання у вашу форму прихованого поля, яке містить значення, яке знаєте лише ви і ваш користувач. Це гарантує, що користувач - а не якась інша сутність - відправляє дані.

Перед використанням CSRF-захисту, встановіть його у вашому проекті:

1
$ composer require symfony/security-csrf

Потім увімкніть/вимкніть CSRF-захист за допомогою опції csrf_protection (див. довідник конфігурації CSRF , щоб дізнатися більше):

1
2
3
4
# config/packages/framework.yaml
framework:
    # ...
    csrf_protection: ~

Токени, які використовуються для CSRF-захисту, мають бути різними для кожного користувача і зберігатися в сесії. Тому сесія розпочинається автоматично, як тільки ви відображаєте форму з CSRF-захистом.

Більш того, це означає, що ви не можете повністю кешувати сторінки, які мають форми з захистом від CSRF. Як варіант, ви можете:

  • Вбудувати форму всередину некешованого фрагмента ESI та кешувати залишок змісту сторінки;
  • Кешувати всю сторінку і завантажити форму через некешований запит AJAX;
  • Кешувати всю сторінку і використати hinclude.js для завантаження CSRF-токена за некешованим запитом AJAX, та замінити значення поля форми ним.

CSRF-захист у формах Symfony

Форми, створені за допомогою компонента Symfony Форма, включають в себе токени CSRF за замовчуванням і Symfony перевіряє їх автоматично, так що вам не потрібно нічого робити, щоб бути захищеним від CSRF атак.

За замовчуванням, Symfony додає CSRF-токен у приховане поле під назвою _token, але це можна налаштовувати від форми до форми:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// src/Form/TaskType.php
namespace App\Form;

// ...
use App\Entity\Task;
use Symfony\Component\OptionsResolver\OptionsResolver;

class TaskType extends AbstractType
{
    // ...

    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class'      => Task::class,
            // увімкнути/вимкнути захист від CSRF для цієї форми
            'csrf_protection' => true,
            // ім'я прихованого HTML поля, що зберігає токен
            'csrf_field_name' => '_token',
            // довільний рядок, використаний для генерування значення токена,
            // використання іншого рядку для кожної форми посилює її безпеку
            'csrf_token_id'   => 'task_item',
        ]);
    }

    // ...
}

Ви також можете налаштувати відображення поля форми CSRF, створивши користувацьку тему форми та використовуючи csrf_token в якості префіксу поля (наприклад, визначити {% block csrf_token_widget %} ... {% endblock %}, щоб налаштувати весь зміст поля форми).

CSRF-захист у формах входу в систему

Див. , щоб побчити форму входу в систему, яка захищена від CSRF-атак. Ви також можете сконфігурувати CSRF-захист для дії виходу з системи .

Генерування та перевірка CSRF-токенів вручну

Хоча Форми Symfony надають автоматичний CSRF-захист за замовчуванням, вам може знадобитися згенерувати та перевірити CSRF-токени вручну, наприклад, при використанні звичайних HTML форм, не керованих компонентом Symfony Форми.

Розгляньте HTML-форму, створену для дозволу на видалення об'єктів. Для початку, використайте функцію Twig csrf_token() , щоб згенерувати CSRF-токен у шаблоні та зберегти його у прихованому полі форми:

1
2
3
4
5
6
<form action="{{ url('admin_post_delete', { id: post.id }) }}" method="post">
    {# аргумент csrf_token() це довільний рядок, який використовується для генерування токена #}
    <input type="hidden" name="token" value="{{ csrf_token('delete-item') }}">

    <button type="submit">Delete item</button>
</form>

Після цього, отримайте значення CSRF-токена у дії контролера та використайте метод isCsrfTokenValid() щоб перевірити його валідність:

1
2
3
4
5
6
7
8
9
10
11
12
13
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
// ...

public function delete(Request $request): Response
{
    $submittedToken = $request->request->get('token');

    // 'delete-item' це те ж значення, що використовується у шаблоні для генерування токена
    if ($this->isCsrfTokenValid('delete-item', $submittedToken)) {
        // ... зробіть щось, на кшталт видалення об'єкта
    }
}

В якості альтернативи ви можете використовувати атрибут IsCsrfTokenValid у дії контролера:

1
2
3
4
5
6
7
8
9
10
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Http\Attribute\IsCsrfTokenValid;
// ...

#[IsCsrfTokenValid('delete-item', tokenKey: 'token')]
public function delete(Request $request): Response
{
    // ... зробити щось, наприклад, видалити обʼєкт
}

7.1

Атрибут IsCsrfTokenValid було представлено в Symfony 7.1.

CSRF-токени і стискання атак за сторонніми каналами

BREACH і CRIME - це експлойти безпеки проти HTTPS при використанні стискання HTTP. Хакери можуть використати інформацію, яка витікла під час стиску, щоб відновити цільові частини відкритого тексту. Щоб послабити ці атаки та запобігти вгадуванню хакером CSRF-токенів, до початку токена додається рандомна маска, яка використовується для його змішування.