安全

安全类包含帮助保护您的网站免受跨站点请求伪造攻击的方法。

加载库

如果您只对加载库来处理 CSRF 防护感兴趣,那么您将永远不需要加载它,因为它作为过滤器运行,并且没有手动交互。

如果您发现需要直接访问的情况,您可以通过 Services 文件加载它

<?php

$security = \Config\Services::security();

跨站点请求伪造 (CSRF)

警告

CSRF 防护仅适用于 **POST/PUT/PATCH/DELETE** 请求。其他方法的请求不受保护。

先决条件

当您使用 CodeIgniter 的 CSRF 保护时,您仍然需要按照以下方式进行编码。否则,CSRF 保护可能会被绕过。

当自动路由被禁用时

执行以下操作之一

  1. 不要使用 $routes->add(),并在路由中使用 HTTP 动词。

  2. 在处理之前检查控制器方法中的请求方法。

例如

if (! $this->request->is('post')) {
    return $this->response->setStatusCode(405)->setBody('Method Not Allowed');
}

注意

自 v4.3.0 起,可以使用 $this->request->is() 方法。在之前的版本中,您需要使用 if (strtolower($this->request->getMethod()) !== 'post')

当自动路由被启用时

  1. 在处理之前检查控制器方法中的请求方法。

例如

if (! $this->request->is('post')) {
    return $this->response->setStatusCode(405)->setBody('Method Not Allowed');
}

CSRF 配置

CSRF 保护方法

警告

如果您使用 Session,请确保使用基于 Session 的 CSRF 保护。基于 Cookie 的 CSRF 保护将无法防止同源攻击。有关详细信息,请参阅 GHSA-5hm8-vh6r-2cjq

默认情况下,使用基于 Cookie 的 CSRF 保护。它是 OWASP 跨站点请求伪造预防备忘单上的 双重提交 Cookie

您也可以使用基于 Session 的 CSRF 保护。它是 同步器令牌模式

您可以通过编辑 **app/Config/Security.php** 中的以下配置参数值来设置使用基于 Session 的 CSRF 保护。

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class Security extends BaseConfig
{
    public $csrfProtection = 'session';

    // ...
}

令牌随机化

为了缓解像 BREACH 这样的压缩侧信道攻击,并防止攻击者猜测 CSRF 令牌,您可以配置令牌随机化(默认情况下关闭)。

如果您启用它,一个随机掩码将被添加到令牌中并用于对其进行混淆。

您可以通过编辑 **app/Config/Security.php** 中的以下配置参数值来启用它。

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class Security extends BaseConfig
{
    public $tokenRandomize = true;

    // ...
}

令牌再生

令牌可以在每次提交时再生(默认)或在会话或 CSRF cookie 的整个生命周期内保持不变。

默认的令牌再生提供了更严格的安全性,但可能会导致可用性问题,因为其他令牌将变得无效(后退/前进导航、多个选项卡/窗口、异步操作等)。您可以通过编辑 **app/Config/Security.php** 中的以下配置参数值来更改此行为。

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class Security extends BaseConfig
{
    public $regenerate = true;

    // ...
}

警告

如果您使用基于 Cookie 的 CSRF 保护,并且在提交后使用 redirect(),则必须调用 withCookie() 来发送再生的 CSRF cookie。有关详细信息,请参阅 重定向

注意

从 v4.2.3 开始,您可以使用 Security::generateHash() 方法手动再生 CSRF 令牌。

失败时的重定向

从 v4.3.0 开始,当请求未能通过 CSRF 验证检查时,它将默认抛出 SecurityException。

注意

在生产环境中,当您使用 HTML 表单时,建议您启用此重定向以获得更好的用户体验。

如果您希望将其重定向到上一页,请更改 **app/Config/Security.php** 中的以下配置参数值。

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class Security extends BaseConfig
{
    // ...

    public bool $redirect = true;

    // ...
}

重定向后,将设置一个 error 闪存消息,您可以在视图中使用以下代码将其显示给最终用户。

<?= session()->getFlashdata('error') ?>

这比简单地崩溃提供了更好的体验。

即使重定向值为 true,AJAX 调用也不会重定向,而是会抛出 SecurityException。

启用 CSRF 保护

您可以通过修改 **app/Config/Filters.php** 并全局启用 csrf 过滤器来启用 CSRF 保护。

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class Filters extends BaseConfig
{
    public $globals = [
        'before' => [
            // 'honeypot',
            'csrf',
        ],
    ];

    // ...
}

可以选择将某些 URI 从 CSRF 保护中排除(例如,期望外部 POST 内容的 API 端点)。您可以通过将这些 URI 添加到过滤器中的例外来添加它们。

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class Filters extends BaseConfig
{
    public $globals = [
        'before' => [
            'csrf' => ['except' => ['api/record/save']],
        ],
    ];

    // ...
}

也支持正则表达式(不区分大小写)。

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class Filters extends BaseConfig
{
    public $globals = [
        'before' => [
            'csrf' => ['except' => ['api/record/[0-9]+']],
        ],
    ];

    // ...
}

也可以只为特定方法启用 CSRF 过滤器。

<?php

namespace Config;

use CodeIgniter\Config\BaseConfig;

class Filters extends BaseConfig
{
    public $methods = [
        'get'  => ['csrf'],
        'post' => ['csrf'],
    ];

    // ...
}

警告

如果您使用 $methods 过滤器,则应该 禁用自动路由(旧版),因为 自动路由(旧版) 允许任何 HTTP 方法访问控制器。使用您不期望的方法访问控制器可能会绕过过滤器。

HTML 表单

如果您使用 表单助手,那么 form_open() 会自动在您的表单中插入一个隐藏的 csrf 字段。

注意

要使用 CSRF 字段的自动生成,您需要在表单页面上启用 CSRF 过滤器。在大多数情况下,它是使用 GET 方法请求的。

如果没有,那么您可以使用始终可用的 csrf_token()csrf_hash() 函数。

<input type="hidden" name="<?= csrf_token() ?>" value="<?= csrf_hash() ?>" />

此外,您可以使用 csrf_field() 方法为您生成这个隐藏的输入字段。

// Generates: <input type="hidden" name="{csrf_token}" value="{csrf_hash}" />
<?= csrf_field() ?>

在发送 JSON 请求时,CSRF 令牌也可以作为参数之一传递。传递 CSRF 令牌的另一种方式是使用一个特殊的 Http 头部,其名称可以通过 csrf_header() 函数获得。

此外,您可以使用 csrf_meta() 方法为您生成这个方便的元标签。

// Generates: <meta name="{csrf_header}" content="{csrf_hash}" />
<?= csrf_meta() ?>

用户发送令牌的顺序

检查 CSRF 令牌可用性的顺序如下所示。

  1. $_POST 数组

  2. HTTP 头部

  3. php://input (JSON 请求) - 请记住,这种方法是最慢的,因为我们必须解码 JSON,然后重新编码它。

  4. php://input (原始主体) - 用于 PUT、PATCH 和 DELETE 类型的请求

注意

从 v4.4.2 版本开始,检查 php://input (原始主体)。

其他有用方法

您将永远不需要直接使用 Security 类中的大多数方法。以下是一些您可能会发现有用的与 CSRF 防护无关的方法。

sanitizeFilename()

尝试清理文件名以防止目录遍历尝试和其他安全威胁,这对于通过用户输入提供的文件特别有用。第一个参数是要清理的路径。

如果用户输入可以包含相对路径,例如 **file/in/some/approved/folder.txt**,您可以将第二个可选参数 $relativePath 设置为 true

<?php

$path = $security->sanitizeFilename($request->getVar('filepath'));