Новое в Symfony 7.1: Атрибут IsCsrfTokenValid

Источник: «New in Symfony 7.1: IsCsrfTokenValid Attribute»
Symfony 7.1 содержит новый атрибут IsCsrfTokenValid для упрощения логики, необходимой для проверки валидности CSRF-токенов.

Атаки CSRF (Подделка межсайтовых запросов) используются злоумышленниками, чтобы заставить ваших легитимных пользователей отправлять данные неосознанно. В Symfony мы обеспечиваем полную защиту от CSRF-атак благодаря компоненту SecurityCsrf.

Ниже приведён распространённый фрагмент кода, используемый в контроллерах, расширяющих AbstractController Symfony, для проверки валидности CSRF-токена формы:

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpKernel\Exception\BadRequestHttpException;

class BlogPostController extends AbstractController
{
// ...

public function delete(): Response
{
if (!$this->isCsrfTokenValid('delete_example', $request->request->getString('_token'))) {
throw new BadRequestHttpException('This token is invalid');
}

// ...
}
}

В Symfony 7.1 вводится новый атрибут #[IsCsrfTokenValid], упрощающий этот код:

// ...
use Symfony\Component\Security\Http\Attribute\IsCsrfTokenValid;

class BlogPostController
{
// ...

#[IsCsrfTokenValid('delete_example')]
public function delete(): Response
{
// ...
}
}

Другая распространённая потребность — проверка нескольких одинаковых CSRF-токенов. Например, в списке статей блога каждая из них имеет немного отличающуюся форму удаления:

{% for post in blog_posts %}
{# ... #}

<form action="{{ path('post_delete', {post: post.id}) }}" method="POST">
<input type="hidden" name="_token" value="{{ csrf_token('delete-post-' ~ post.id) }}">
</form>
{% endfor %}

Эту проблему также можно решить с помощью атрибута #[IsCsrfTokenValid], поскольку он поддерживает выражения, совместимые с компонентом ExpressionLanguage:

// ...
use Symfony\Component\ExpressionLanguage\Expression;

class BlogPostController
{
// ...

#[IsCsrfTokenValid(new Expression('"delete-post-" ~ args["post"].id'))]
public function delete(Request $request, Post $post): Response
{
// ...
}
}

Дополнительные материалы

Предыдущая Статья

Новое в Symfony 7.1: Компонент TypeInfo

Следующая Статья

Проблема с new URL(), и как URL.parse() её решает