# Symfony CSRF 教程

> 原文： [http://zetcode.com/symfony/csrf/](http://zetcode.com/symfony/csrf/)

Symfony CSRF 教程展示了如何在 Symfony 应用中实现 CSRF 保护。

## Symfony

Symfony 是一组可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。 Symfony 于 2005 年发布为免费软件。Symfony 的原始作者是 Fabien Potencier。 Symfony 的灵感来自 Djanog，Spring 和 ROR 框架。

## CSRF

跨站点请求伪造（CSRF）是一种攻击，其中恶意用户试图使合法用户在不知情的情况下提交他们不打算提交的数据。 CSRF 攻击专门针对状态更改请求，而不是数据盗窃。 成功的 CSRF 攻击可以迫使用户执行状态更改请求，例如转移资金或更改其个人资料详细信息。

CSRF 保护的工作原理是在表单中添加一个隐藏字段，该字段包含仅应用和用户知道的值（令牌）。 这样可以确保用户（而非其他实体）正在提交给定的数据。

`symfony/security-csrf`组件提供`CsrfTokenManager`用于生成和验证 CSRF 令牌。 默认情况下，使用 Symfony 表单组件创建的表单包括 CSRF 令牌，Symfony 会自动检查它们，因此我们无需采取任何措施来防止 CSRF 攻击。 `csrf_token()` Twig 函数为用户呈现 CSRF 令牌。

## Symfony CSRF 保护示例

在下面的示例中，我们手动创建一个表单，为其实施 CSRF 保护。 在此应用中，我们在`routes.yaml`文件中定义路由。

```php
$ composer create-project symfony/skeleton csrf-app
$ cd csrf-app

```

使用`composer`，我们创建一个新的 Symfony 框架项目并定位到项目目录。

```php
$ composer require symfony/security-csrf

```

我们安装`security-csrf`软件包。

```php
$ composer require server --dev

```

我们安装开发 Web 服务器。

`config/routes.yaml`

```php
index:
    path: /
    controller: App\Controller\AppController::index

process-form:
    path: /process
    controller: App\Controller\AppController::processForm

```

我们为应用定义了两条路由。 `index`路由显示带有表单的主页。 `process-form`处理提交的表单并检查 CSRF 令牌。

`src/Controller/AppController.php`

```php
<?php

namespace App\Controller;

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;

class AppController extends AbstractController
{
    public function index()
    {
        return $this->render('home/index.html.twig');
    }

    public function processForm(Request $request)
    {
        $token = $request->request->get("token");

        if (!$this->isCsrfTokenValid('myform', $token))
        {
            return new Response('Operation not allowed', Response::HTTP_BAD_REQUEST,
                ['content-type' => 'text/plain']);
        }

        $name = $request->request->get("name");
        $email = $request->request->get("email");

        $msg = "$name with $email saved";

        return new Response($msg, Response::HTTP_CREATED, ['content-type' => 'text/plain']);
    }
}

```

`AppController`具有两个动作：`index()`和`processForm()`。

```php
public function index()
{
    return $this->render('home/index.html.twig');
}

```

`index()`功能呈现主页。 主页包含 HTML 表单。

```php
$token = $request->request->get("token");

```

我们从请求中使用`get()`方法检索 CSRF 令牌。

```php
if (!$this->isCsrfTokenValid('myform', $token))
{
    return new Response('Operation not allowed', Response::HTTP_BAD_REQUEST,
        ['content-type' => 'text/plain']);
}

```

我们使用`isCsrfTokenValid()`方法检查令牌的有效性。 如果令牌无效，我们将返回带有`Response::HTTP_BAD_REQUEST`代码的响应。 令牌`myform`的名称在模板的 HTML 表单中指定。

`templates/home/index.html.twig`

```php
{% extends 'base.html.twig' %}

{% block title %}Home page{% endblock %}

{% block body %}

    <section class="ui container">

        <form class="ui form" action="{{ path('process-form') }}" method="post">

            <input type="hidden" name="token" value="{{ csrf_token('myform') }}" />

            <div class="field">
                <label>Name:</label>
                <input name="name" type="text">
            </div>

            <div class="field">
                <label>Email</label>
                <input name="email" type="text">
            </div>

            <button class="ui button" type="submit">Send</button>

        </form>

    </section>

{% endblock %}

```

这是带有表单的主页的 Twig 模板。 表单使用语义 UI 库进行样式设置。

```php
<form class="ui form" action="{{ path('process-form') }}" method="post">

```

表单动作指向`process-form`路径。 表单的方法是 POST，这意味着必须进行 CSRF 保护。

```php
<input type="hidden" name="token" value="{{ csrf_token('myform') }}" />

```

我们使用 CSRF 令牌添加隐藏的输入。 令牌是使用`csrf_token()`生成的。

`templates/base.html.twig`

```php
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>
        {% block title %}Welcome!
        {% endblock %}
    </title>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css" 
        rel="stylesheet">
</head>

<body>
    {% block body %}{% endblock %}
</body>

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.js"></script>
</html>

```

这是基本模板文件。 它加载语义 UI 库。

```php
$ php bin/console server:run

```

我们运行该应用。

```php
$ curl -d "name=Peter&email=peter@example.com" -X POST http://localhost:8000/process
Operation not allowed

```

如果我们尝试绕过表单并尝试使用`curl`工具访问控制器操作，则会收到错误消息。

在本教程中，我们在 Symfony 应用中实现了 CSRF 保护。

您可能也对以下相关教程感兴趣： [Symfony 验证教程](/symfony/validation/)， [Symfony 表单教程](/symfony/form/)， [PHP 教程](/lang/php/)或列出[所有 Symfony 教程](/all/#symfony)。