Skip to content

Instantly share code, notes, and snippets.

@valorin
Last active July 10, 2024 12:46
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.
<?php
$production = env('APP_ENV', 'production') === 'production';
return [
// Toggle Report-Only based on production
'report_only' => ! $production,
// 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://larasec.substack.com/p/in-depth-content-security-policy
*/
class CSP
{
public function handle($request, Closure $next)
{
Vite::useCspNonce();
$response = $next($request);
$csp = config('csp');
$csp['policy']['script-src'][] = "'nonce-".Vite::cspNonce()."'";
$csp['policy']['style-src'][] = "'nonce-".Vite::cspNonce()."'";
if (Vite::isRunningHot()) {
$vite = Vite::asset('');
$csp['policy']['connect-src'][] = 'wss:'.Str::after($vite, 'https:');
$csp['policy']['script-src'][] = $vite;
$csp['policy']['style-src'][] = $vite;
}
$policy = collect($csp['policy'])
->filter()
->map(fn ($value, $key) => "{$key} ".collect($value)->filter()->implode(' '))
->implode(' ; ');
$header = $csp['report_only'] ? 'Content-Security-Policy-Report-Only' : 'Content-Security-Policy';
$response->headers->set($header, $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