安全
安全类包含帮助保护您的网站免受跨站点请求伪造攻击的方法。
加载库
如果您只对加载库来处理 CSRF 防护感兴趣,那么您将永远不需要加载它,因为它作为过滤器运行,并且没有手动交互。
如果您发现需要直接访问的情况,您可以通过 Services 文件加载它
<?php
$security = \Config\Services::security();
跨站点请求伪造 (CSRF)
警告
CSRF 防护仅适用于 **POST/PUT/PATCH/DELETE** 请求。其他方法的请求不受保护。
先决条件
当您使用 CodeIgniter 的 CSRF 保护时,您仍然需要按照以下方式进行编码。否则,CSRF 保护可能会被绕过。
当自动路由被禁用时
执行以下操作之一
不要使用
$routes->add()
,并在路由中使用 HTTP 动词。在处理之前检查控制器方法中的请求方法。
例如
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')
。
当自动路由被启用时
在处理之前检查控制器方法中的请求方法。
例如
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 令牌可用性的顺序如下所示。
$_POST
数组HTTP 头部
php://input
(JSON 请求) - 请记住,这种方法是最慢的,因为我们必须解码 JSON,然后重新编码它。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'));