ZetCode

Symfony CSRF

最后修改于 2020 年 7 月 5 日

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

Symfony

Symfony 是一套可重用的 PHP 组件和一个用于 Web 项目的 PHP 框架。Symfony 于 2005 年以自由软件的形式发布。Symfony 的灵感来自 Django、Spring 和 ROR 框架。

CSRF

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

CSRF 保护必须应用于被认为是“不安全”的 HTTP 请求。安全方法不需要 CSRF 保护,因为它们不会更改应用程序。有关更多详细信息,请参阅 should-i-use-csrf-protection-for-get-requests

CSRF 保护通过在表单中添加一个隐藏字段来实现,该字段包含一个只有应用程序和用户知道的值(令牌)。这确保了是用户而不是其他实体提交了给定的数据。

symfony/security-csrf 组件提供了 CsrfTokenManager 用于生成和验证 CSRF 令牌。使用 Symfony Form 组件和表单构建器创建的表单默认包含 CSRF 令牌,Symfony 会自动检查它们。在这种情况下,我们无需采取任何措施即可防范 CSRF 攻击。

如果我们不使用表单组件或表单构建器,我们就需要自己处理 CSRF(使用 Symfony 工具)。

csrf_token() Twig 指令会为用户渲染 CSRF 令牌。

Symfony CSRF 保护示例

在以下示例中,我们将手动创建一个表单并为其实现 CSRF 保护。在此应用程序中,我们在 routes.yaml 文件中定义路由。

$ symfony new mycsrf
$ cd mycsrf

使用 symfony CLI,我们创建一个新的 Symfony skeleton 项目并进入项目目录。

$ composer req twig symfony/security-csrf

我们安装 twigsecurity-csrf 包。

config/routes.yaml
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

namespace App\Controller;

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

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

    public function processForm(Request $request): Response
    {
        $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 received";

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

AppController 包含两个动作:index()processForm()

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

index() 函数渲染主页。主页包含 HTML 表单。

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

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

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
{% 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 模板。表单使用 Semantic UI 库进行样式设置。

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

表单的 action 指向 process-form 路径。表单的方法是 POST,这意味着需要 CSRF 保护。

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

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

templates/base.html.twig
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>{% block title %}Welcome!{% endblock %}</title>
        {% block stylesheets %}
            <link href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.4.1/semantic.min.css"
                  rel="stylesheet">
        {% endblock %}
    </head>
    <body>
        {% block body %}{% endblock %}
        {% block javascripts %}{% 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>

这是基础模板文件。它加载 Semantic UI 库。

$ symfony serve

我们运行应用程序。

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

如果我们尝试绕过表单并使用 curl 工具访问控制器动作,我们会收到一条错误消息。

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

列出 所有 Symfony 教程。