paragonie/csp-builder
Build and send Content-Security-Policy headers in PHP from JSON files, JSON strings, or arrays. CSP Builder makes it easy to define directives programmatically and integrate CSP into web apps to improve security.
Installation:
composer require paragonie/csp-builder
Add to composer.json if using Laravel’s require-dev for testing.
First Use Case:
Create a JSON config file (config/csp.json):
{
"default-src": ["'self'"],
"script-src": ["'self'", "https://cdn.example.com"]
}
Load and send in a Laravel middleware or service:
use ParagonIE\CSPBuilder\CSPBuilder;
$csp = CSPBuilder::fromFile(base_path('config/csp.json'));
$csp->sendCSPHeader();
Where to Look First:
addSource, hash, nonce).app()->make(CSPBuilder::class) or bind the builder in AppServiceProvider for dependency injection.Create a dedicated CSP middleware (app/Http/Middleware/CSP.php):
public function handle($request, Closure $next) {
$csp = app(CSPBuilder::class);
$response = $next($request);
$csp->injectCSPHeader($response);
return $response;
}
Register in app/Http/Kernel.php:
protected $middleware = [
\App\Http\Middleware\CSP::class,
];
Use Cases:
config/csp/production.json).Route::middleware(['csp'])->group(function () {
$csp = app(CSPBuilder::class);
$csp->addSource('script-src', "'self'", "'unsafe-inline'"); // Admin-only inline scripts
});
$nonce = $csp->nonce('script-src');
echo "<script nonce=\"$nonce\">console.log('Safe!');</script>";
$hash = $csp->preHash('script-src', file_get_contents('script.js'), 'sha256');
$csp->addSource('script-src', $hash);
Configure report-uri in JSON:
{
"report-uri": "/csp-report-endpoint",
"report-to": "csp-reports"
}
Handle reports in Laravel (routes/web.php):
Route::post('/csp-report-endpoint', [CSPReportController::class, 'store']);
For frameworks like Lumen or custom PSR-7 apps:
$response = new \Zend\Diactoros\Response();
$csp->injectCSPHeader($response);
Generate Nginx/Apache snippets for static CSP headers:
$csp->saveSnippet(
storage_path('app/nginx/csp.conf'),
CSPBuilder::FORMAT_NGINX
);
Include in server config:
location / {
add_header Content-Security-Policy always;
include snippets/csp.conf;
}
Duplicate Directives:
addSource multiple times for the same directive may cause duplicates.setDirective to overwrite or addSource with unique values.$csp->setDirective('script-src', ['self', 'https://cdn.example.com']); // Overwrites
Nonce/Hash Scope:
script-src nonce won’t work for style-src).$nonce = $csp->nonce('style-src'); // For CSS
Report-To vs. Report-Uri:
report-to over report-uri. Use both for fallback:
{
"report-to": "csp-reports",
"report-uri": "/fallback-report-endpoint"
}
PHP Version:
paragonie/csp-builder:^2.9 for PHP 7.0–7.3 support.Semicolon Injection:
addDirective).$csp->addSource('img-src', ['self', 'https://trusted.cdn.com']);
Inspect Headers:
Use browser dev tools (Network tab) or Laravel’s dd($response->headers) to verify CSP headers.
Test in Report-Only Mode:
Temporarily set "report-only": true in JSON to test policies without blocking resources.
CSP Evaluator: Validate policies at CSP Evaluator.
Custom Directives:
Extend CSPBuilder to support non-standard directives (e.g., trusted-types):
$csp->addDirective('trusted-types', ['script']);
Hooks for Output: Modify generated CSP before saving:
$csp->saveSnippet(
'path/to/snippet.conf',
CSPBuilder::FORMAT_NGINX,
fn($output) => str_replace("'self'", "'none'", $output)
);
Laravel Service Provider: Bind the builder with environment-specific configs:
$this->app->bind(CSPBuilder::class, function ($app) {
$config = config('csp.' . config('app.env'));
return new CSPBuilder($config);
});
CSPBuilder instances across requests (stateless after initialization).fromFile in Loops: Load JSON configs once (e.g., in AppServiceProvider boot method).unsafe-inline/unsafe-eval: Avoid unless absolutely necessary. Use nonces/hashes instead.child-src Deprecation: Use frame-ancestors for modern browsers (v3.0.0+ un-deprecated frame-src as an alias).How can I help you explore Laravel packages today?