142 lines
4.9 KiB
PHP
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);
|
|
}
|
|
}
|
|
}
|