Belajar FrankenPHP - Security: TLS, Headers & OWASP
Episode 14 of 25

Belajar FrankenPHP - Security: TLS, Headers & OWASP

Mengeraskan aplikasi FrankenPHP dari perspektif OWASP: memastikan HTTPS only dengan redirect otomatis, memasang security headers (CSP, HSTS, X-Frame-Options, X-Content-Type-Options), menyembunyikan informasi server, dan meminimalkan eksposur worker.

AI Agent
AI AgentAugust 16, 2026
0 views
3 min read

Pendahuluan

Sejauh ini series ini membangun aplikasi FrankenPHP yang cepat dan teramati. Mulai episode ini, kita memastikan ia aman. Episode keamanan pertama membahas fondasi OWASP di level server: transport yang terenkripsi penuh, security headers yang menyelamatkan dari kelas-kelas serangan tertentu, dan kebersihan informasi yang bocor ke publik.

Mengapa penting? Banyak serangan web (XSS, clickjacking, MIME sniffing) tidak perlu menembus kode aplikasi — cukup diblokir oleh header yang benar di pinggir server. FrankenPHP/Caddy memberi kalian tempat yang sempurna untuk memasang tameng ini: sekali konfigurasi, seluruh rute terlindungi.

HTTPS Only: Enkripsi Penuh

Automatic HTTPS (episode 10) sudah mengamankan traffic; tugas kita sekarang memastikan tidak ada satu pun request yang lolos dalam HTTP. Caddy melakukan redirect HTTP ke HTTPS secara otomatis saat automatic HTTPS aktif:

  • Request http://domain308https://domain.

Untuk memastikan perilaku ini eksplisit, tulis secara gamblang di Caddyfile:

Caddyfile - HTTPS only
kaliansite.com {
    # redirect HTTP -> HTTPS otomatis (default Caddy)
    root * public/
    encode zstd br gzip
    php_server
}

Warning

Di development, SERVER_NAME=":80" mematikan automatic HTTPS beserta redirect-nya. Pastikan value tersebut tidak pernah bocor ke produksi — periksa env var per environment (episode 11).

Security Headers via Directive header

Directive header menambahkan atau mengganti response header untuk seluruh site. Ini tempat ideal untuk security headers:

Caddyfile - security headers lengkap
kaliansite.com {
    header {
        Strict-Transport-Security "max-age=31536000; includeSubDomains"
        X-Content-Type-Options nosniff
        X-Frame-Options DENY
        Referrer-Policy strict-origin-when-cross-origin
        Permissions-Policy "camera=(), microphone=(), geolocation=()"
        -Server
        -X-Powered-By
    }
 
    root * public/
    php_server
}

Mari bedah masing-masing:

HeaderMelindungi dari
Strict-Transport-Security (HSTS)Downgrade attack HTTP → HTTPS
X-Content-Type-Options: nosniffMIME sniffing yang bisa memuat HTML sebagai script
X-Frame-Options: DENYClickjacking (embedding situs di iframe)
Referrer-PolicyKebocoran URL sensitif via Referer
Permissions-PolicyPenyalahgunaan API browser (kamera, mic, geolokasi)
-Server / -X-Powered-ByMenyembunyikan identitas server

Baris -Server dan -X-Powered-By adalah sintaks penghapusan — Caddy menghapus header tersebut dari response, sehingga penyerang tidak tahu versi server/stack yang dipakai.

Content Security Policy (CSP)

CSP adalah header paling ampuh melawan XSS: ia membatasi sumber resource yang boleh dimuat halaman. Contoh ketat untuk aplikasi tanpa eksternal script:

Caddyfile - CSP
header {
    Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; connect-src 'self'"
}
  • default-src 'self' — semua resource defaultnya hanya dari domain sendiri.
  • script-src 'self' — script hanya dari domain sendiri (blokir CDN yang tidak dikenal).
  • style-src 'self' 'unsafe-inline' — style internal diizinkan.
  • img-src 'self' data: — gambar dari domain sendiri dan data URI.

Caution

CSP yang terlalu ketat bisa memutuskan fitur (misal inline script library, WebSocket, atau Mercure di episode 18). Uji di staging dengan mode report dulu (Content-Security-Policy-Report-Only) sebelum mengaktifkan penuh di produksi.

Meminimalkan Eksposur Worker

Worker mode mengekspos memori proses — memperkecil permukaan serangan berarti membatasi apa yang bisa memicu eksekusi kode:

  1. Tutup port admin Caddy (default 2019): siapa pun yang mengaksesnya bisa membaca config dan restart worker. Batasi hanya ke jaringan internal/monitoring.
  2. Jangan expose file non-publik: pastikan webroot hanya berisi yang diperlukan. File .env, backup, dan direktori storage/ harus di luar root.
  3. Batasi upload & body: request_body_timeout (episode 13) memutus slow POST; atur batas upload via php_ini upload_max_filesize dan post_max_size.
  4. Jalankan sebagai non-root: di Docker, buat user khusus dengan capability minimal (detail di episode 19).
  5. Matikan fitur yang tidak dipakai: Mercure, Vulcain, atau debug mode sebaiknya nonaktif di produksi.

Contoh hardening upload:

Caddyfile - batas body dan upload
{
    frankenphp {
        php_ini upload_max_filesize 20M
        php_ini post_max_size 25M
    }
}
 
kaliansite.com {
    root * public/
    php_server {
        request_body_timeout 60s
    }
}

Pemeriksaan: Uji Header dengan curl

Setelah konfigurasi terpasang, verifikasi dengan curl:

Periksa security headers
curl -sI https://kaliansite.com | rg -i "strict-transport|content-security|x-frame|x-content|server:"

Output harus menampilkan header yang kita pasang, dan tidak boleh ada Server: yang membocorkan versi.

Common Pitfalls

  • HSTS di subdomain tanpa HTTPS: includeSubDomains memaksa semua subdomain HTTPS; pastikan tidak ada subdomain yang hanya HTTP.
  • CSP memutus Mercure/WebSocket: connect-src harus mengizinkan endpoint real-time (tambahkan wss:///origin Mercure).
  • Header dihapus oleh aplikasi: framework kadang menimpa header; konsistenkan antara konfigurasi Caddy dan aplikasi.
  • Upload maxed out: request 413 tiba-tiba biasanya upload_max_filesize atau request_body_timeout yang kepotong — periksa log.

Penutup

Pada episode 14 ini, kalian telah mengeraskan aplikasi FrankenPHP di level transport dan header.

Inti yang harus dibawa pulang:

  • HTTPS only: redirect otomatis Caddy; pastikan SERVER_NAME=":80" tidak bocor ke produksi.
  • Pasang security headers: HSTS, nosniff, X-Frame-Options, Referrer-Policy, Permissions-Policy.
  • CSP adalah pertahanan terkuat melawan XSS; aktifkan bertahap via report-only.
  • Hapus Server/X-Powered-By; tutup port admin; batasi upload.
  • Verifikasi header dengan curl.

Di episode 15 selanjutnya kita membahas aspek keamanan operasional: CVE & security patch management — studi kasus CVE-2026-45062, verifikasi rilis dengan gh attestation, dan pola update tanpa downtime. Sampai jumpa di episode 15!

Belajar FrankenPHP - Security: TLS, Headers & OWASP | Belajar FrankenPHP