ldc-boutique-cnc-sable/app/Http/Middleware/SecureHeaders.php
2026-10-27 09:58:12 +01:00

142 lines
4.9 KiB
PHP

<?php
declare(strict_types=1);
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Vite;
use Symfony\Component\HttpFoundation\Response;
class SecureHeaders
{
/**
* Enumerate headers which you do not want in your application's responses.
* Great starting point would be to go check out @Scott_Helme's:
* https://securityheaders.com/
*
* @var string[]
*/
private $unwantedHeaderList = [
'X-Powered-By',
'Server',
];
/**
* Handle an incoming request.
*
* @param \Closure(\Illuminate\Http\Request):(\Illuminate\Http\Response) $next
*/
public function handle(Request $request, Closure $next): Response
{
$appUrl = Config::string('app.url');
$appDebug = Config::boolean('app.debug');
$telescopeEnabled = Config::boolean('telescope.enabled', false);
// * For Vite DEV
$host = \parse_url($appUrl, \PHP_URL_HOST);
$viteDevOrigin = $appDebug && \is_string($host) && $host !== ''
? "https://{$host}:4443"
: '';
$viteDevSocket = $appDebug && \is_string($host) && $host !== ''
? "wss://{$host}:4443"
: '';
Vite::useCspNonce(Vite::cspNonce());
$nonce = Vite::cspNonce();
app()->instance('csp_nonce', $nonce);
$jsBo = $request->routeIs('bo*') ? "'unsafe-eval'" : '';
$cspReportUri = route('cspReportUri');
$reportUri =
\sprintf(' report-to default; report-uri %s ;', \parse_url($cspReportUri, \PHP_URL_PATH));
$jsdelivrUrl = 'https://cdn.jsdelivr.net';
$openStreetMapTiles = 'https://*.tile.openstreetmap.org';
$this->removeUnwantedHeaders($this->unwantedHeaderList);
$response = $next($request);
$response->headers->set('Referrer-Policy', 'no-referrer-when-downgrade');
$response->headers->set('X-Content-Type-Options', 'nosniff');
$response->headers->set('X-XSS-Protection', '1; mode=block');
$response->headers->set('X-Frame-Options', 'SAMEORIGIN');
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
// * Ignore CSP on some conditions.
if (
$appDebug && (
$response->isClientError() || // Ignition
$response->isServerError() || // Ignition
Vite::isRunningHot() || // Vite
($telescopeEnabled && str_starts_with($request->path(), 'telescope')) // Telescope
)
) {
return $response;
}
$cspPolicies = [
"default-src 'none';",
"base-uri 'self';",
"manifest-src 'self';",
// * CKEDITOR Specific https://ckeditor.com/docs/ckeditor5/latest/installation/advanced/csp.html
"connect-src 'self' " . implode(' ', [
'https://yl.s-web.fr/',
// * BAN address autocomplete (api-adresse.data.gouv.fr)
'https://api-adresse.data.gouv.fr',
// * VITE DEV
$viteDevSocket,
$viteDevOrigin,
]) . ';',
"img-src 'self' data: blob: " . implode(' ', [
$viteDevOrigin,
$openStreetMapTiles,
]) . ' data:;',
'child-src ' . implode(' ', []) . ';',
"object-src 'none';",
"font-src 'self' data: " . \implode(' ', [
// * VITE DEV
$viteDevOrigin,
]) . ';',
// ! Using CKEditor will must have unsafe-inline style
"style-src 'self' 'unsafe-inline' " . \implode(' ', [
$jsdelivrUrl,
// * File Manager
$jsdelivrUrl,
// * VITE DEV
$viteDevOrigin,
]) . ';',
// ! If you dont use ckeditor use this
// "style-src 'self' 'nonce-$nonce';",
"script-src 'self' 'nonce-$nonce' 'strict-dynamic' " . \implode(' ', [
'https: blob:',
$jsBo,
// * VITE DEV
$viteDevOrigin,
]) . ';',
];
$response->headers->set(
'Content-Security-Policy',
implode(' ', $cspPolicies) . $reportUri
);
$response->headers->set('Feature-Policy', 'fullscreen \'self\'; geolocation \'self\';');
$response->headers->set('Reporting-Endpoints', 'default="' . $cspReportUri . '"');
return $response;
}
/**
* Remove unwanted headers
*
* @param string[] $headerList List of headers to remove from the response
* @return void
*/
private function removeUnwantedHeaders(array $headerList)
{
foreach ($headerList as $header) {
header_remove($header);
}
}
}