5e35fefd69
API CI/CD / Validate (composer + pint) (push) Successful in 2m47s
API CI/CD / Test (PHPUnit) (push) Failing after 3m8s
API CI/CD / Build frontend assets (push) Failing after 5m22s
API CI/CD / Security audit (push) Failing after 34s
API CI/CD / Deploy to shared hosting (PHP) (push) Has been skipped
45 lines
1.4 KiB
PHP
45 lines
1.4 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Middleware;
|
|
|
|
use Closure;
|
|
use Illuminate\Http\Request;
|
|
use Symfony\Component\HttpFoundation\Response;
|
|
|
|
class SecurityHeaders
|
|
{
|
|
public function handle(Request $request, Closure $next): Response
|
|
{
|
|
$requestId = (string) $request->headers->get('X-Request-Id', '');
|
|
if ($requestId !== '' && ! preg_match('/^[A-Za-z0-9._:-]{1,128}$/', $requestId)) {
|
|
$response = response()->json(['message' => 'Invalid request id.'], 400);
|
|
$this->applySecurityHeaders($request, $response);
|
|
|
|
return $response;
|
|
}
|
|
|
|
/** @var Response $response */
|
|
$response = $next($request);
|
|
$this->applySecurityHeaders($request, $response);
|
|
|
|
if ($requestId !== '') {
|
|
$response->headers->set('X-Request-Id', $requestId);
|
|
}
|
|
|
|
return $response;
|
|
}
|
|
|
|
private function applySecurityHeaders(Request $request, Response $response): void
|
|
{
|
|
$headers = $response->headers;
|
|
$headers->set('X-Content-Type-Options', 'nosniff');
|
|
$headers->set('X-Frame-Options', 'DENY');
|
|
$headers->set('Referrer-Policy', 'strict-origin-when-cross-origin');
|
|
$headers->set('Permissions-Policy', 'camera=(), microphone=(), geolocation=()');
|
|
|
|
if ($request->isSecure()) {
|
|
$headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
|
|
}
|
|
}
|
|
}
|