CI4 如何为开发和生产环境分别配置 ContentSecurityPolicy.php?
CI4 不会仅凭 Config\development\ContentSecurityPolicy.php 和 Config\production\ContentSecurityPolicy.php 的存在,就自动按环境切换配置。可以在 app/Config/ContentSecurityPolicy.php 中根据 ENVIRONMENT 设置不同指令。如果要保留独立文件,需要在这个配置类中显式加载。
为什么分目录放置没有生效?
这种做法假设环境目录会自动覆盖配置,但 CI4 使用配置类,通常从 app/Config 获取配置,并通过环境变量覆盖相应属性。单独创建环境子目录不会让配置自动切换。CI4 配置说明
启用 CSP 后,响应对象会读取 app/Config/ContentSecurityPolicy.php 中的配置。因此,可以在该配置类中判断环境,也可以由它加载其他策略文件。CSP 配置说明
方案一:直接在配置类中判断环境
如果两套策略只有少量差异,可以在现有的 Config\ContentSecurityPolicy 类中添加下面的构造方法。保留文件中的其他配置属性;如果已有构造方法,就将逻辑合并进去。
public function __construct()
{
$this->connectSrc = ['self'];
if (ENVIRONMENT === 'development') {
$this->connectSrc = [
'self',
'http://localhost:5173',
'ws://localhost:5173',
];
}
parent::__construct();
}
这里假设开发环境需要连接 localhost:5173 上的 HTTP 服务和 WebSocket 服务。地址和端口仅作示例,应替换为项目实际使用的值;如果没有这些连接需求,就不要添加。
这段代码只调整 connect-src,其他指令继续使用现有配置。如果开发环境还需要从本地服务加载 JavaScript 或 CSS,也要分别调整 scriptSrc、styleSrc 等对应属性。
示例先设置环境默认值,再调用 parent::__construct()。这样 BaseConfig 可以完成初始化,匹配的环境变量也能随后覆盖默认值。构造方法中的执行顺序决定覆盖优先级。BaseConfig 实现
方案二:将策略保存在独立文件中
如果策略较多,可以保留一个配置类入口,将环境差异拆分到返回数组的 PHP 文件中。下面是自定义加载方式,目录名称不是 CI4 的特殊约定。
创建 app/Config/CSP/development.php:
<?php
return [
'connectSrc' => [
'self',
'http://localhost:5173',
'ws://localhost:5173',
],
];
创建 app/Config/CSP/production.php:
<?php
return [
'connectSrc' => ['self'],
];
然后在现有的 app/Config/ContentSecurityPolicy.php 配置类中使用下面的构造方法,替换方案一的构造方法:
public function __construct()
{
$file = ENVIRONMENT === 'development'
? 'development.php'
: 'production.php';
$policy = require __DIR__ . '/CSP/' . $file;
$this->connectSrc = $policy['connectSrc'];
parent::__construct();
}
这个示例只为 development 加载开发策略,其他环境使用生产策略。如果 testing 或自定义环境需要不同配置,应增加明确的映射。
两个拆分文件只返回配置数据,无须重复声明 Config\ContentSecurityPolicy 类。需要拆分更多指令时,可以在数组中添加对应字段,再在构造方法中赋给相应属性。
设置环境并确认生效
开发环境的项目根目录 .env 中设置:
CI_ENVIRONMENT = development
生产环境设置:
CI_ENVIRONMENT = production
应用运行时通过 ENVIRONMENT 常量获取当前环境。如果部署平台也设置了 CI_ENVIRONMENT,应确认实际运行值符合预期。环境配置说明
同时确认 app/Config/App.php 中已有的 $CSPEnabled 属性设置为 true,并保留项目原有的属性类型声明。启用 CSP
最后,在浏览器开发者工具的 Network 面板中打开页面请求,检查响应头中的 Content-Security-Policy。按上述示例配置且没有后续覆盖时,开发环境的 connect-src 应包含示例中的本地地址,生产环境应只包含 'self'。
如果实际响应头仍与配置不同,检查控制器、过滤器、Web 服务器或反向代理是否继续修改了 CSP。验证策略时还应留意 Debug Toolbar:官方文档说明它可能因 nonce 处理影响 CSP 响应头,必要时关闭后再检查。CSP 调试注意事项
备注:内容仅供参考。
