本文目录导读:

我来详细介绍ThinkPHP项目中实施CSP(内容安全策略)的方法:
CSP基础配置
1 通过中间件添加CSP头
在app/middleware.php中注册CSP中间件:
<?php
// app/middleware.php
return [
// 全局CSP中间件
\app\middleware\CspMiddleware::class,
];
2 创建CSP中间件
<?php
// app/middleware/CspMiddleware.php
namespace app\middleware;
use Closure;
use think\Request;
use think\Response;
class CspMiddleware
{
/**
* CSP策略配置
*/
protected $cspConfig = [
'default-src' => "'self'",
'script-src' => "'self' 'unsafe-inline' 'unsafe-eval'",
'style-src' => "'self' 'unsafe-inline' https://cdn.example.com",
'img-src' => "'self' data: https://*.example.com",
'font-src' => "'self' https://fonts.googleapis.com https://fonts.gstatic.com",
'connect-src' => "'self'",
'frame-src' => "'self'",
'object-src' => "'none'",
'base-uri' => "'self'",
'form-action' => "'self'",
'frame-ancestors' => "'self'",
'upgrade-insecure-requests' => '',
];
public function handle(Request $request, Closure $next)
{
$response = $next($request);
// 构建CSP头部
$cspHeader = $this->buildCspHeader();
// 添加CSP响应头
$response->header([
'Content-Security-Policy' => $cspHeader,
'X-Content-Security-Policy' => $cspHeader, // 旧浏览器兼容
'X-WebKit-CSP' => $cspHeader, // WebKit浏览器兼容
]);
return $response;
}
/**
* 构建CSP头部字符串
*/
protected function buildCspHeader(): string
{
$directives = [];
foreach ($this->cspConfig as $key => $value) {
if ($value !== '') {
$directives[] = "{$key} {$value}";
} else {
$directives[] = $key;
}
}
return implode('; ', $directives);
}
}
动态CSP配置
1 配置文件方式
<?php
// config/csp.php
return [
// 开发环境配置
'development' => [
'default-src' => "'self'",
'script-src' => "'self' 'unsafe-inline' 'unsafe-eval'",
'style-src' => "'self' 'unsafe-inline'",
'img-src' => "'self' data:",
'connect-src' => "'self' ws://localhost:*",
],
// 生产环境配置
'production' => [
'default-src' => "'self'",
'script-src' => "'self'",
'style-src' => "'self'",
'img-src' => "'self' data:",
'connect-src' => "'self'",
'font-src' => "'self' https://fonts.googleapis.com https://fonts.gstatic.com",
'object-src' => "'none'",
'base-uri' => "'self'",
'form-action' => "'self'",
'frame-ancestors' => "'self'",
'upgrade-insecure-requests' => '',
],
];
2 改进中间件支持配置
<?php
// app/middleware/CspMiddleware.php
namespace app\middleware;
use Closure;
use think\Request;
use think\Response;
use think\facade\Config;
class CspMiddleware
{
protected $cspConfig = [];
protected $noncePool = [];
public function __construct()
{
// 从配置文件加载CSP设置
$environment = app()->isDebug() ? 'development' : 'production';
$this->cspConfig = Config::get('csp.' . $environment);
// 生成nonce值
$this->generateNonces();
}
/**
* 生成CSP nonce
*/
protected function generateNonces(): void
{
$this->noncePool = [
'script' => base64_encode(random_bytes(16)),
'style' => base64_encode(random_bytes(16)),
];
}
public function handle(Request $request, Closure $next)
{
$response = $next($request);
// 应用CSP头部
$this->applyCspHeaders($response);
// 注入nonce到响应
if ($response instanceof Response && !$request->isAjax()) {
$this->injectNonces($response);
}
return $response;
}
/**
* 应用CSP头
*/
protected function applyCspHeaders(Response $response): void
{
$cspHeader = $this->buildCspHeader();
$response->header([
'Content-Security-Policy' => $cspHeader,
'X-Content-Security-Policy' => $cspHeader,
'X-WebKit-CSP' => $cspHeader,
]);
}
/**
* 构建CSP头
*/
protected function buildCspHeader(): string
{
$directives = [];
foreach ($this->cspConfig as $key => $value) {
// 替换nonce占位符
if (strpos($value, '{nonce}') !== false) {
$value = str_replace('{nonce}', "'nonce-{$this->noncePool[$key] ?? ''}'", $value);
}
$directives[] = $value === '' ? $key : "{$key} {$value}";
}
return implode('; ', $directives);
}
/**
* 注入nonce到HTML
*/
protected function injectNonces(Response $response): void
{
$content = $response->getContent();
// 替换script标签
$content = preg_replace(
'/<script(?!.*nonce)/i',
'<script nonce="' . $this->noncePool['script'] . '"',
$content
);
// 替换style标签
$content = preg_replace(
'/<style(?!.*nonce)/i',
'<style nonce="' . $this->noncePool['style'] . '"',
$content
);
$response->content($content);
}
}
视图中的CSP集成
1 在模板中使用nonce
<!-- application/index/view/index/index.html -->
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">ThinkPHP CSP</title>
<!-- 使用nonce的样式 -->
<style nonce="<?= $csp['style'] ?>">
body {
font-family: Arial, sans-serif;
margin: 20px;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background: #f5f5f5;
border-radius: 5px;
}
</style>
<!-- 内联脚本使用nonce -->
<script nonce="<?= $csp['script'] ?>">
document.addEventListener('DOMContentLoaded', function() {
console.log('页面加载完成');
});
</script>
</head>
<body>
<div class="container">
<h1>内容安全策略演示</h1>
<!-- 外部脚本 -->
<script src="/static/js/app.js" nonce="<?= $csp['script'] ?>"></script>
<!-- 内联事件处理器可能需要额外处理 -->
<button onclick="handleClick()" data-nonce="<?= $csp['script'] ?>">点击我</button>
</div>
</body>
</html>
2 控制器中传递nonce
<?php
// app/controller/Index.php
namespace app\controller;
use think\Request;
class Index
{
public function index(Request $request)
{
// 获取中间件生成的nonce
$csp = [
'script' => $request->middleware('nonce_script'),
'style' => $request->middleware('nonce_style'),
];
return view('index/index', ['csp' => $csp]);
}
}
高级CSP配置
1 CSP报告功能
<?php
// app/middleware/CspReportMiddleware.php
namespace app\middleware;
use Closure;
use think\Request;
class CspReportMiddleware
{
protected $reportUri = '/api/csp/report';
public function handle(Request $request, Closure $next)
{
$response = $next($request);
// 添加报告URI
$response->header('Content-Security-Policy-Report-Only',
"default-src 'self'; report-uri {$this->reportUri}");
return $response;
}
}
// app/controller/api/CspReport.php
namespace app\controller\api;
use think\Request;
use think\facade\Log;
class CspReport
{
public function report(Request $request)
{
// 获取CSP违规报告
$data = $request->post();
// 记录违规信息
Log::error('CSP Violation:', $data);
// 可以在数据库中记录
// 可以发送邮件通知
return json(['status' => 'success']);
}
}
2 按路由应用不同策略
<?php
// route/app.php
use think\facade\Route;
// 管理路由使用更严格的CSP
Route::group('admin', function () {
Route::get('dashboard', 'admin/Dashboard/index');
Route::get('settings', 'admin/Settings/index');
})->middleware(\app\middleware\StrictCspMiddleware::class);
// API路由使用CORS配置
Route::group('api', function () {
// API路由
})->middleware(\app\middleware\ApiCspMiddleware::class);
// 前端路由使用宽松CSP
Route::group('frontend', function () {
// 前端路由
})->middleware(\app\middleware\FrontendCspMiddleware::class);
3 完整的CSP配置类
<?php
// app/library/Csp/CspFactory.php
namespace app\library\Csp;
class CspFactory
{
/**
* 创建严格CSP
*/
public static function strict(): array
{
return [
'default-src' => "'none'",
'script-src' => "'self'",
'style-src' => "'self'",
'img-src' => "'self' data:",
'connect-src' => "'self'",
'font-src' => "'self'",
'object-src' => "'none'",
'frame-ancestors' => "'none'",
'base-uri' => "'self'",
'form-action' => "'self'",
];
}
/**
* 创建一般CSP
*/
public static function standard(): array
{
return [
'default-src' => "'self'",
'script-src' => "'self' 'unsafe-inline'",
'style-src' => "'self' 'unsafe-inline'",
'img-src' => "'self' data: https:",
'font-src' => "'self' https:",
'connect-src' => "'self'",
'object-src' => "'none'",
'frame-ancestors' => "'self'",
'base-uri' => "'self'",
'form-action' => "'self'",
];
}
/**
* 创建宽松CSP
*/
public static function relaxed(): array
{
return [
'default-src' => "'self' *",
'script-src' => "'self' 'unsafe-inline' 'unsafe-eval' *",
'style-src' => "'self' 'unsafe-inline' *",
'img-src' => "'self' data: blob: *",
'font-src' => "'self' data: *",
'connect-src' => "'self' *",
'object-src' => "'none'",
'frame-ancestors' => "*",
];
}
}
4 动态CSP适配器
<?php
// app/middleware/AdaptiveCspMiddleware.php
namespace app\middleware;
use Closure;
use think\Request;
use think\Response;
use app\library\Csp\CspFactory;
class AdaptiveCspMiddleware
{
protected $cspConfig = [];
public function handle(Request $request, Closure $next)
{
// 根据用户代理和路由动态选择CSP策略
$this->cspConfig = $this->selectCspPolicy($request);
$response = $next($request);
// 应用CSP头
$cspHeader = $this->buildCspHeader();
$response->header('Content-Security-Policy', $cspHeader);
return $response;
}
/**
* 选择CSP策略
*/
protected function selectCspPolicy(Request $request): array
{
// 判断是否为后台管理页面
if (strpos($request->pathinfo(), 'admin') === 0) {
return CspFactory::strict();
}
// 判断是否为API请求
if (strpos($request->pathinfo(), 'api') === 0) {
return CspFactory::api();
}
// 判断是否为移动端
$userAgent = $request->header('user-agent');
if (preg_match('/Mobile|Android|iPhone/i', $userAgent)) {
return CspFactory::mobile();
}
// 默认使用标准策略
return CspFactory::standard();
}
/**
* 构建CSP头
*/
protected function buildCspHeader(): string
{
$directives = [];
foreach ($this->cspConfig as $directive => $value) {
$directives[] = "{$directive} {$value}";
}
return implode('; ', $directives);
}
}
测试和调试
1 CSP测试页面
<?php
// app/controller/CspTest.php
namespace app\controller;
use think\Request;
class CspTest
{
public function index()
{
$scripts = [
'inline' => '<script>console.log("测试内联脚本");</script>',
'external' => '<script src="/static/js/test.js"></script>',
'external_cdn' => '<script src="https://cdn.example.com/js/lib.js"></script>'
];
return view('csp/test', ['scripts' => $scripts]);
}
public function report(Request $request)
{
$report = json_decode($request->getContent(), true);
// 日志记录
trace($report, 'CSP Violation');
return json(['status' => 'ok']);
}
}
2 CSP监控配置
<?php
// config/csp_monitor.php
return [
'enabled' => true,
'log_level' => 'warning',
'report_threshold' => 10, // 每分钟报告次数阈值
'report_endpoint' => '/api/csp/report',
'allowed_scripts' => [
'https://cdn.example.com',
'https://statistics.example.com',
],
'allowed_styles' => [
'https://fonts.googleapis.com',
],
'allowed_images' => [
'https://*.example.com',
],
];
3 CSP安全检查工具
<?php
// app/library/Csp/CspValidator.php
namespace app\library\Csp;
class CspValidator
{
/**
* 验证CSP策略
*/
public static function validate(array $policy): array
{
$errors = [];
$warnings = [];
// 检查是否配置了默认策略
if (!isset($policy['default-src'])) {
$errors[] = '缺少 default-src 指令';
}
// 检查是否允许了危险的指令
foreach (['script-src', 'style-src'] as $directive) {
if (isset($policy[$directive])) {
if (strpos($policy[$directive], "'unsafe-inline'") !== false) {
$warnings[] = "{$directive} 包含 unsafe-inline";
}
if (strpos($policy[$directive], "'unsafe-eval'") !== false) {
$warnings[] = "{$directive} 包含 unsafe-eval";
}
}
}
// 检查是否配置了报告URI
if (!isset($policy['report-uri']) && !isset($policy['report-to'])) {
$warnings[] = '未配置报告URI,无法监控违规行为';
}
return [
'errors' => $errors,
'warnings' => $warnings,
'valid' => empty($errors),
];
}
}
最佳实践建议
1 部署前检查清单
<?php
// app/command/CheckCsp.php
namespace app\command;
use think\console\Command;
use think\console\Input;
use think\console\Output;
use app\library\Csp\CspFactory;
use app\library\Csp\CspValidator;
class CheckCsp extends Command
{
protected function configure()
{
$this->setName('check:csp')
->setDescription('检查CSP配置');
}
protected function execute(Input $input, Output $output)
{
$output->writeln('开始检查CSP配置...');
// 检查所有环境的CSP配置
foreach (['development', 'production', 'test'] as $env) {
$output->writeln("检查{$env}环境:");
$config = Config::get('csp.' . $env);
$result = CspValidator::validate($config);
if (!$result['valid']) {
foreach ($result['errors'] as $error) {
$output->writeln(" <error>[错误] {$error}</error>");
}
}
foreach ($result['warnings'] as $warning) {
$output->writeln(" <comment>[警告] {$warning}</comment>");
}
if (empty($result['errors']) && empty($result['warnings'])) {
$output->writeln(" <info>[OK] 配置良好</info>");
}
}
// 检查静态资源
$this->checkStaticResources($output);
$output->writeln('CSP检查完成');
}
protected function checkStaticResources(Output $output)
{
$output->writeln('检查静态资源...');
// 检查JavaScript文件
$jsFiles = glob(root_path('public/static/js/*.js'));
foreach ($jsFiles as $file) {
$content = file_get_contents($file);
if (preg_match('/\beval\s*\(/i', $content)) {
$output->writeln(" <comment>[警告] 文件包含eval: {$file}</comment>");
}
}
}
}
2 安全建议
// 1. 不在CSP中添加过多的域名白名单 // 2. 使用nonce而不是allowlist管理内联脚本 // 3. 定期检查CSP报告 // 4. 使用Report-Only模式测试 // 5. 为不同环境设置不同的CSP策略 // 6. 考虑使用SRI(子资源完整性)
通过以上配置,你可以为ThinkPHP项目建立完善的内容安全策略,有效防范XSS攻击和数据注入攻击,记得根据实际需求调整配置,并在部署前充分测试。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播