setHeader('X-Frame-Options', 'SAMEORIGIN'); // X-Content-Type-Options: Mencegah MIME type sniffing // nosniff = browser tidak boleh menebak content type $response->setHeader('X-Content-Type-Options', 'nosniff'); // X-XSS-Protection: Legacy header untuk browser lama (optional) // Mode=block = block page jika XSS terdeteksi $response->setHeader('X-XSS-Protection', '1; mode=block'); // Referrer-Policy: Kontrol informasi referrer yang dikirim // strict-origin-when-cross-origin = kirim full URL untuk same-origin, // hanya origin untuk cross-origin HTTPS, tidak ada untuk HTTP $response->setHeader('Referrer-Policy', 'strict-origin-when-cross-origin'); // Permissions-Policy: Kontrol fitur browser yang bisa digunakan // Membatasi akses ke fitur seperti geolocation, camera, microphone, dll // Hanya gunakan feature yang didukung oleh browser modern $permissionsPolicy = [ 'geolocation=()', // Geolocation API 'camera=()', // Camera access 'microphone=()', // Microphone access 'payment=()', // Payment Request API 'usb=()', // WebUSB API 'magnetometer=()', // Magnetometer sensor 'gyroscope=()', // Gyroscope sensor 'accelerometer=()', // Accelerometer sensor 'ambient-light-sensor=()', // Ambient light sensor 'autoplay=()', // Autoplay media 'fullscreen=()', // Fullscreen API 'picture-in-picture=()', // Picture-in-picture ]; $response->setHeader('Permissions-Policy', implode(', ', $permissionsPolicy)); // ============================================================ // HSTS (HTTP Strict Transport Security) // ============================================================ // Hanya aktif di production dengan HTTPS // max-age=31536000 = 1 tahun // includeSubDomains = berlaku untuk semua subdomain // preload = bisa ditambahkan ke HSTS preload list (optional) if ($isProduction) { $response->setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains'); } // ============================================================ // CONTENT SECURITY POLICY (CSP) // ============================================================ // CSP directives untuk mencegah XSS attacks // Konfigurasi disesuaikan untuk TailAdmin dan Alpine.js // // CATATAN: Alpine.js memerlukan 'unsafe-eval' untuk mengevaluasi // expression JavaScript (x-data, x-show, dll). Ini trade-off security // yang diperlukan untuk Alpine.js bekerja dengan baik. $cspDirectives = [ "default-src 'self'", // Default: hanya dari same origin "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net", // Script: allow inline dan eval untuk Alpine.js "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com", // Style: allow inline untuk Tailwind "font-src 'self' data: https://fonts.gstatic.com", // Font: allow data URI dan Google Fonts "img-src 'self' data: https:", // Image: allow data URI dan HTTPS "connect-src 'self'", // AJAX/Fetch: hanya same origin "frame-ancestors 'self'", // Frame: hanya same origin "base-uri 'self'", // Base URI: hanya same origin "form-action 'self'", // Form action: hanya same origin "object-src 'none'", // Object/embed: tidak ada ]; // Hanya tambahkan upgrade-insecure-requests di production if ($isProduction) { $cspDirectives[] = "upgrade-insecure-requests"; } $cspValue = implode('; ', $cspDirectives); if ($isProduction) { // Enforce CSP di production $response->setHeader('Content-Security-Policy', $cspValue); } else { // Report-Only di development untuk testing $response->setHeader('Content-Security-Policy-Report-Only', $cspValue); // Juga set regular CSP untuk security audit tools $response->setHeader('Content-Security-Policy', $cspValue); } // ============================================================ // ADDITIONAL SECURITY HEADERS // ============================================================ // Cross-Origin-Embedder-Policy: Mencegah embedding dari cross-origin // require-corp = require Cross-Origin Resource Policy // $response->setHeader('Cross-Origin-Embedder-Policy', 'require-corp'); // Optional, bisa break beberapa fitur // Cross-Origin-Opener-Policy: Isolasi browsing context // same-origin = hanya same-origin yang bisa access window // $response->setHeader('Cross-Origin-Opener-Policy', 'same-origin'); // Optional // Cross-Origin-Resource-Policy: Kontrol resource sharing // same-origin = hanya same-origin yang bisa load resource // $response->setHeader('Cross-Origin-Resource-Policy', 'same-origin'); // Optional return $response; } }