Security Headers

Security Headers #

Security headers adalah respons HTTP headers yang memberitahu browser bagaimana berperilaku saat menampilkan kontenmu — apakah boleh dimuat dalam iframe, protokol mana yang boleh di-request, resource apa yang boleh dimuat, dan banyak lagi. Mengkonfigurasi security headers yang tepat bisa mencegah berbagai serangan web termasuk XSS, clickjacking, MIME sniffing, dan information disclosure.

Caddy membuat penambahan security headers sangat mudah melalui direktif header.

Header Security Esensial #

example.com {
    header {
        # ── Proteksi Transport ────────────────────────────────────
        # Paksa HTTPS untuk semua request selama 2 tahun
        # includeSubDomains: berlaku juga untuk semua subdomain
        # preload: daftarkan ke HSTS preload list browser
        Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
        
        # ── Proteksi Konten ───────────────────────────────────────
        # Cegah browser menebak MIME type (MIME sniffing)
        X-Content-Type-Options "nosniff"
        
        # ── Proteksi Framing ──────────────────────────────────────
        # Cegah halaman dimuat dalam iframe (clickjacking protection)
        # DENY: tidak bisa di-iframe sama sekali
        # SAMEORIGIN: bisa di-iframe hanya dari domain yang sama
        X-Frame-Options "SAMEORIGIN"
        
        # ── Privacy ───────────────────────────────────────────────
        # Kontrol informasi Referer yang dikirim ke site lain
        Referrer-Policy "strict-origin-when-cross-origin"
        
        # ── Sembunyikan Info Server ────────────────────────────────
        -Server
        -X-Powered-By
    }
    
    file_server {
        root /var/www/html
    }
}

HSTS — HTTP Strict Transport Security #

HSTS memberi tahu browser bahwa site ini hanya boleh diakses via HTTPS, bahkan jika user mengetik http://:

example.com {
    header {
        # max-age=31536000     → 1 tahun
        # max-age=63072000     → 2 tahun (direkomendasikan)
        # includeSubDomains    → berlaku untuk SEMUA subdomain
        # preload              → minta browser pre-load HSTS (permanen!)
        Strict-Transportation-Security "max-age=63072000; includeSubDomains; preload"
    }
}
Hati-hati dengan preload! Setelah domain masuk ke HSTS preload list browser, sangat sulit dihapus (bisa butuh berbulan-bulan). Pastikan kamu berkomitmen untuk selalu menggunakan HTTPS di domain dan SEMUA subdomain sebelum menambahkan preload. Mulai dengan max-age=300 (5 menit) untuk testing, naikkan ke nilai besar setelah yakin.

Content Security Policy (CSP) #

CSP adalah header yang paling powerful sekaligus paling kompleks. Ia mendefinisikan sumber resource yang boleh dimuat oleh browser:

example.com {
    header {
        # CSP yang ketat untuk aplikasi modern
        Content-Security-Policy "
            default-src 'self';
            script-src 'self' 'nonce-{http.request.uuid}' https://cdn.jsdelivr.net;
            style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
            img-src 'self' data: https: blob:;
            font-src 'self' https://fonts.gstatic.com;
            connect-src 'self' https://api.example.com wss://ws.example.com;
            media-src 'self';
            object-src 'none';
            frame-src 'none';
            frame-ancestors 'none';
            base-uri 'self';
            form-action 'self';
            upgrade-insecure-requests;
        "
    }
}

Penjelasan Direktif CSP #

Direktif           Arti
──────────────────────────────────────────────────────────────────────────────
default-src        Default untuk semua resource (fallback)
script-src         Dari mana JavaScript boleh dimuat
style-src          Dari mana CSS boleh dimuat
img-src            Dari mana gambar boleh dimuat
font-src           Dari mana font boleh dimuat
connect-src        URL yang boleh dihubungi via XHR, fetch, WebSocket
media-src          Dari mana video/audio boleh dimuat
object-src         Dari mana plugin object (<object>, <embed>) boleh dimuat
frame-src          Dari mana iframe boleh dimuat
frame-ancestors    Siapa yang boleh mem-frame halaman ini (gantikan X-Frame-Options)
base-uri           Batasi URL yang bisa digunakan di <base> tag
form-action        Batasi URL yang bisa menjadi tujuan form submit
upgrade-insecure-requests  Upgrade HTTP ke HTTPS otomatis

Nilai sumber:
'self'             Domain yang sama
'none'             Tidak diizinkan dari mana pun
'unsafe-inline'    Inline script/style (kurangi keamanan)
'unsafe-eval'      Eval() dan fungsi sejenisnya (kurangi keamanan)
https:             Semua HTTPS URL
data:              Data URI
blob:              Blob URI
URL spesifik       https://trusted-cdn.com
nonce-{value}      Script/style dengan nonce yang cocok
sha256-{hash}      Script/style dengan hash yang cocok

CSP Report-Only untuk Testing #

Sebelum mengaktifkan CSP secara penuh, gunakan report-only mode untuk melihat apa yang akan diblokir:

example.com {
    header {
        # Report-Only: tidak memblokir, hanya melaporkan violations
        Content-Security-Policy-Report-Only "
            default-src 'self';
            script-src 'self' https://cdn.jsdelivr.net;
            report-uri https://csp.example.com/reports;
        "
        
        # Setelah yakin tidak ada false positive, ganti dengan:
        # Content-Security-Policy "..."
    }
}

Permissions Policy #

Menggantikan Feature Policy, header ini mengontrol akses ke fitur browser:

example.com {
    header {
        # Nonaktifkan fitur yang tidak digunakan aplikasimu
        Permissions-Policy "
            camera=(),
            microphone=(),
            geolocation=(),
            payment=(),
            usb=(),
            magnetometer=(),
            accelerometer=(),
            gyroscope=(),
            fullscreen=(self),
            display-capture=()
        "
    }
}

Menghapus Header yang Mengekspos Info Server #

example.com {
    header {
        # Header yang mengekspos teknologi stack
        -Server           # Caddy/2.x.x
        -X-Powered-By     # PHP/8.x, Express, dll.
        -X-AspNet-Version
        -X-AspNetMvc-Version
        
        # Header yang tidak diperlukan
        -X-Runtime
        -X-Request-Id     # Jika tidak diperlukan client
    }
    
    reverse_proxy backend:3000
}

Set Header di Level Reverse Proxy vs Level Site #

example.com {
    # Level site: berlaku untuk SEMUA response termasuk error Caddy sendiri
    header Strict-Transport-Security "max-age=31536000"
    header X-Frame-Options "SAMEORIGIN"
    
    reverse_proxy backend:3000 {
        # Level reverse proxy: hanya untuk response dari backend
        # Berguna jika ingin override/tambahkan header spesifik untuk backend
        header_down X-Backend-Version "v2.1.0"
        header_down -X-Powered-By      # Hapus dari response backend
    }
}

Template Lengkap Security Headers #

(security_headers) {
    header {
        # Transport security
        Strict-Transport-Security "max-age=63072000; includeSubDomains"
        
        # Content security
        X-Content-Type-Options "nosniff"
        
        # Framing protection
        X-Frame-Options "SAMEORIGIN"
        
        # Referrer policy
        Referrer-Policy "strict-origin-when-cross-origin"
        
        # Permissions (minimal — sesuaikan dengan kebutuhan app)
        Permissions-Policy "camera=(), microphone=(), geolocation=()"
        
        # Sembunyikan server info
        -Server
        -X-Powered-By
    }
}

# Gunakan snippet di semua site
example.com {
    import security_headers
    file_server { root /var/www/html }
}

api.example.com {
    import security_headers
    reverse_proxy localhost:8080
}

Testing Security Headers #

# Test dengan curl
curl -I https://example.com | grep -i \
    "strict-transport\|x-frame\|x-content-type\|referrer\|content-security"

# Atau gunakan tools online:
# https://securityheaders.com
# https://observatory.mozilla.org
# https://www.ssllabs.com/ssltest/

# Cek skor SSL/TLS
# ssllabs.com memberikan grade A+ untuk konfigurasi Caddy default

# Test lokal dengan verbose
curl -sv https://example.com 2>&1 | grep "< " | grep -v "^< $"

Ringkasan #

  • HSTS (Strict-Transport-Security) memaksa browser selalu pakai HTTPS — mulai dengan max-age=300 untuk testing, naikkan ke nilai besar (63072000 = 2 tahun) setelah yakin.
  • CSP (Content-Security-Policy) adalah header paling powerful untuk mencegah XSS — gunakan Report-Only mode dulu sebelum mengaktifkan penuh.
  • X-Frame-Options: SAMEORIGIN mencegah clickjacking — atau gunakan frame-ancestors 'self' di CSP untuk kontrol yang lebih granular.
  • Selalu hapus header informatif (-Server, -X-Powered-By) untuk menyembunyikan teknologi stack dari attacker.
  • Gunakan snippet (security_headers) untuk menghindari duplikasi konfigurasi header yang sama di banyak site.
  • Test security headers dengan securityheaders.com dan observatory.mozilla.org — targetkan grade A atau A+.

← Sebelumnya: Pembatasan IP   Berikutnya: CORS →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact