Security 类
Security 类提供用于防范跨站请求伪造(CSRF)攻击的方法。
加载类
如仅需通过加载该库来处理 CSRF 防护,则无需手动加载, 因其以过滤器形式运行,无需手动交互。
如确有直接访问需求,可通过 Services 文件加载:
<?php
$security = service('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');
}
备注
$this->request->is() 方法自 v4.3.0 起可用。
早期版本需使用
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;
// ...
}
令牌重新生成
令牌可在每次提交时重新生成(默认),或 在 Session 或 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.5.0 起,当请求未通过 CSRF 验证检查时,默认情况下, 生产环境中用户将被重定向到上一页,其他环境中将抛出 SecurityException。
备注
生产环境中使用 HTML 表单时,建议 启用此重定向以获得更好的用户体验。
升级用户应检查其配置文件。
如希望重定向到上一页,在 app/Config/Security.php 中将以下配置参数值设为 true:
<?php
namespace Config;
use CodeIgniter\Config\BaseConfig;
class Security extends BaseConfig
{
// ...
public bool $redirect = true;
// ...
}
重定向时,会设置 error 闪存消息,可在视图中通过以下代码显示给最终用户:
<?= session()->getFlashdata('error') ?>
这比直接崩溃提供了更好的体验。
即使 redirect 值为 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 端点)。可在过滤器中将其添加为例外:
<?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() 方法生成此隐藏 input 字段:
// 生成:<input type="hidden" name="{csrf_token}" value="{csrf_hash}" />
<?= csrf_field() ?>
发送 JSON 请求时,CSRF 令牌也可作为参数之一传递。
传递 CSRF 令牌的另一种方式是使用特殊 HTTP 标头,其名称可通过 csrf_header() 函数获取。
此外,可使用 csrf_meta() 方法生成 meta 标签:
// 生成:<meta name="{csrf_header}" content="{csrf_hash}" />
<?= csrf_meta() ?>
用户发送令牌的顺序
检查 CSRF 令牌可用性的顺序如下:
$_POST数组HTTP 标头
php://input(JSON 请求)— 注意此方式最慢,因为需要解码 JSON 然后重新编码php://input(原始 body)— 用于 PUT、PATCH 和 DELETE 类型请求
备注
自 v4.4.2 起,会检查 php://input (原始 body)。
其他实用方法
大部分 Security 类方法无需直接使用。以下是一些与 CSRF 防护无关但可能有用的方法。
sanitizeFilename()
尝试清理文件名以防止目录遍历尝试和其他安全威胁, 对通过用户输入提供的文件特别有用。第一个参数为要清理的路径。
如允许用户输入包含相对路径,例如 file/in/some/approved/folder.txt,可将第二个可选参数 $relativePath 设为 true。
<?php
$path = $security->sanitizeFilename($request->getVar('filepath'));
此方法是 Security 辅助函数中 sanitize_filename() 函数的别名。