Skip to content

Instantly share code, notes, and snippets.

@valorin
Last active November 11, 2024 17:59
Show Gist options
  • Save valorin/d4cb9daa190fdee90603efaa8cbc5886 to your computer and use it in GitHub Desktop.
Save valorin/d4cb9daa190fdee90603efaa8cbc5886 to your computer and use it in GitHub Desktop.
CSP Middleware - the simple CSP middleware I use across all of my projects.
# Add to your .env.example and .env files
CSP_ENABLED=true
CSP_REPORT_ONLY=true
<?php
return [
// Toggle CSP on or off
'enabled' => env('CSP_ENABLED', true),
// Toggle Report-Only based on production
'report_only' => env('CSP_REPORT_ONLY', true),
// Policy directives
'policy' => [
// Defaults to Report URI CSP Wizard URL, which is the easiest way to add a CSP to an existing site.
// Check it out at: https://report-uri.com/
'report-uri' => ["https://<subdomain>.report-uri.com/r/d/csp/wizard"],
'default-src' => ["'none'"],
'connect-src' => ["'none'"],
'font-src' => ["'none'"],
'frame-src' => ["'none'"],
'img-src' => ["'self'"],
'manifest-src' => ["'self'"],
'script-src' => ["'report-sample'", "'self'"],
'style-src' => ["'self'"],
'form-action' => ["'none'"],
'frame-ancestors' => ["'none'"],
],
];
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Support\Facades\Vite;
use Illuminate\Support\Str;
/**
* Simple Content Security Policy middleware.
*
* Provides a super simple way to add a CSP to a Laravel app.
* Simply add the required directives to your config/csp.php file.
* Hooks directly into Vite to generate a Nonce for scripts and styles.
*
* !!! Don't forget to add the CSP middleware into the 'web' middleware group !!!
*
* @author Stephen Rees-Carter <https://stephenreescarter.net/>
* @see https://securinglaravel.com/tag/csp/
*/
class CSP
{
public function handle($request, Closure $next)
{
$config = config('csp');
$response = $next($request);
// Ignore if CSP is disabled
if (! $config['enabled']) {
return $response;
}
// Add a nonce to the CSP for scripts and styles through Vite
Vite::useCspNonce();
$config['policy']['script-src'][] = "'nonce-".Vite::cspNonce()."'";
$config['policy']['style-src'][] = "'nonce-".Vite::cspNonce()."'";
// Add Vite server to CSP if running in hot/dev mode
if (Vite::isRunningHot()) {
$vite = Vite::asset('');
$config['policy']['connect-src'][] = 'wss:'.Str::after($vite, 'https:');
$config['policy']['script-src'][] = $vite;
$config['policy']['style-src'][] = $vite;
}
// Generate the CSP header
$policy = collect($config['policy'])
->filter()
->map(fn ($value, $key) => "{$key} ".collect($value)->filter()->implode(' '))
->implode(' ; ');
// Set the CSP header
$response->headers->set(
$config['report_only'] ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy',
$policy
);
return $response;
}
}
<!-- Include the nonce with the globally shared $cspNonce variable -->
<script src="https://evilhacker.dev/script.js" nonce="{{ Vite::cspNonce() }}" defer></script>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment