Files
kaffeekasse-saas/app/Services/SecurityHeadersService.php
T
2026-06-15 18:36:57 +02:00

98 lines
2.8 KiB
PHP

<?php
namespace App\Services;
final class SecurityHeadersService
{
public function buildHeaders(?bool $isHttps = null): array
{
$isHttps = $isHttps ?? $this->isHttpsRequest();
$headers = [
'Content-Security-Policy' => $this->buildContentSecurityPolicy($isHttps),
'Referrer-Policy' => 'strict-origin-when-cross-origin',
'Permissions-Policy' => 'accelerometer=(), autoplay=(), camera=(), display-capture=(), geolocation=(), gyroscope=(), magnetometer=(), microphone=(), payment=(), usb=()',
'Cross-Origin-Opener-Policy' => 'same-origin',
'Cross-Origin-Resource-Policy' => 'same-origin',
'Origin-Agent-Cluster' => '?1',
'X-Content-Type-Options' => 'nosniff',
'X-Frame-Options' => 'SAMEORIGIN',
'X-Permitted-Cross-Domain-Policies' => 'none',
];
if ($isHttps) {
$headers['Strict-Transport-Security'] = 'max-age=31536000; includeSubDomains';
}
return $headers;
}
public function send(array $overrides = [], ?bool $isHttps = null): void
{
if (headers_sent()) {
return;
}
$headers = $this->buildHeaders($isHttps);
foreach ($overrides as $name => $value) {
if (!is_string($name) || trim($name) === '') {
continue;
}
if ($value === null) {
unset($headers[$name]);
continue;
}
$headers[$name] = (string) $value;
}
header_remove('X-Powered-By');
foreach ($headers as $name => $value) {
header($name . ': ' . $value, true);
}
}
private function buildContentSecurityPolicy(bool $isHttps): string
{
$directives = [
"default-src 'self'",
"base-uri 'self'",
"form-action 'self'",
"frame-ancestors 'self'",
"object-src 'none'",
"connect-src 'self'",
"font-src 'self' data:",
"img-src 'self' data:",
"manifest-src 'self'",
"script-src 'self'",
"style-src 'self'",
];
if ($isHttps) {
$directives[] = 'upgrade-insecure-requests';
}
return implode('; ', $directives);
}
private function isHttpsRequest(): bool
{
$https = $_SERVER['HTTPS'] ?? null;
if (is_string($https) && $https !== '' && strtolower($https) !== 'off') {
return true;
}
$forwardedProto = $_SERVER['HTTP_X_FORWARDED_PROTO'] ?? '';
if (is_string($forwardedProto) && strtolower($forwardedProto) === 'https') {
return true;
}
return (string) ($_SERVER['SERVER_PORT'] ?? '') === '443';
}
}