Apache mod_headers ile HTTP Güvenlik Başlıkları Nasıl Yapılandırılır

Web sunucunuzun güvenliğini sadece firewall ve SSL sertifikasıyla sağladığınızı düşünüyorsanız, işin aslında çok daha fazlası olduğunu söylemem gerekiyor. HTTP güvenlik başlıkları, tarayıcı tarafında bir savunma katmanı oluşturur ve XSS, clickjacking, MIME sniffing gibi saldırıları büyük ölçüde engeller. Apache’nin mod_headers modülü ise bu başlıkları yönetmenin en doğrudan yoludur. Bu yazıda, production ortamında gerçekten işe yarayan konfigürasyonları adım adım ele alacağız.

mod_headers Nedir ve Neden Önemlidir

Apache’nin mod_headers modülü, HTTP istek ve yanıt başlıklarını eklemenize, değiştirmenize veya silmenize olanak tanır. Bu modül olmadan güvenlik başlıklarını sunucu tarafında kontrol etmek neredeyse imkansızdır. Uygulama kodunuza her başlığı tek tek ekleyebilirsiniz ama bu hem zaman alır hem de bakımı zorlaşır. Merkezi bir Apache konfigürasyonundan tüm sayfalar için bu başlıkları yönetmek çok daha temiz bir yaklaşımdır.

Gerçek dünya senaryosuna bakacak olursak: Bir e-ticaret sitesi düşünün. Kullanıcı girişi, ödeme sayfaları, admin paneli var. Uygulama geliştiricisi her PHP dosyasına header() çağrısı eklemek yerine Apache seviyesinde bu işi bir kere yapıyor ve tüm uygulama otomatik olarak korunmuş oluyor. Hem standartlaşma hem de güvenlik açısından bu yaklaşım çok daha sağlam.

Modülü Etkinleştirmek

Debian/Ubuntu tabanlı sistemlerde mod_headers genellikle kurulu gelir ama etkin olmayabilir.

# Modülü etkinleştir
sudo a2enmod headers

# Apache'yi yeniden başlat
sudo systemctl restart apache2

# Modülün yüklenip yüklenmediğini kontrol et
apache2ctl -M | grep headers

RHEL/CentOS/AlmaLinux sistemlerinde ise modül genellikle varsayılan olarak yüklü ve etkin gelir. Kontrol etmek için:

# httpd modüllerini listele
httpd -M | grep headers

# Eğer aktif değilse httpd.conf dosyasında şu satırın yorum dışı olduğundan emin ol
grep -i "mod_headers" /etc/httpd/conf/httpd.conf

Eğer headers_module (shared) çıktısını görüyorsanız her şey hazır demektir.

Temel Güvenlik Başlıkları ve Konfigürasyonu

X-Frame-Options

Bu başlık, sitenizin bir içinde yüklenmesini engeller. Clickjacking saldırılarına karşı temel savunma hattınızdır. Kullanıcının, farkında olmadan başka bir sayfanın üzerine yerleştirilmiş görünmez bir çerçevedeki sitenize tıklamasını engeller.

# Virtual host veya .htaccess içinde
Header always set X-Frame-Options "SAMEORIGIN"
  • DENY: Hiçbir çerçevelemeye izin verme
  • SAMEORIGIN: Sadece aynı alan adından çerçevelemeye izin ver
  • ALLOW-FROM uri: Belirli bir URI’dan çerçevelemeye izin ver (modern tarayıcılarda artık desteklenmiyor)

Eğer sitenizde hiç iframe kullanmıyorsanız DENY daha güvenlidir. Kendi alt alan adlarınızdan iframe kullanıyorsanız SAMEORIGIN tercih edin.

X-Content-Type-Options

MIME tipi sniffing saldırılarını engeller. Tarayıcılar bazen sunucunun belirttiği içerik tipini görmezden gelerek dosyanın içeriğine bakıp “bu aslında JavaScript olabilir” gibi yorumlar yapabilir. Bu başlık bunu engeller.

Header always set X-Content-Type-Options "nosniff"

Bu başlık sayesinde bir saldırgan, .jpg uzantısıyla yüklediği bir JavaScript dosyasını çalıştıramaz. E-posta gönderme veya dosya yükleme özelliği olan her sitede bu başlık zorunlu olmalıdır.

Strict-Transport-Security (HSTS)

HTTPS kullanıyorsanız bu başlık şarttır. Tarayıcıya “bu siteye bir daha HTTP ile gelme, her zaman HTTPS kullan” demektedir.

# Temel HSTS - 1 yıllık süre
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"

# Preload listesine eklemek istiyorsanız
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

Dikkat: Bu başlığı eklemeden önce tüm alt alan adlarınızın HTTPS çalıştığından emin olun. includeSubDomains parametresi tüm alt alan adlarını kapsayacaktır. Yanlış yapılandırırsanız bazı alt alan adlarınıza erişilemez hale gelir. preload parametresini eklemek istiyorsanız hstspreload.org üzerinden başvurmanız gerekir.

Content-Security-Policy (CSP)

CSP, sitenizin hangi kaynaklardan içerik yükleyebileceğini tanımlar. XSS saldırılarına karşı en güçlü savunmadır ancak konfigürasyonu en karmaşık olanıdır.

# Başlangıç için katı ama pratik bir CSP
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://trusted-cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https://fonts.gstatic.com; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action 'self'"

CSP direktiflerini açıklayalım:

  • default-src ‘self’: Varsayılan olarak sadece kendi alan adından kaynak yükle
  • script-src ‘self’ ‘unsafe-inline’: JavaScript için kendi alan adı ve inline scriptlere izin ver
  • style-src ‘self’ ‘unsafe-inline’: CSS için benzer kurallar
  • img-src ‘self’ data: https: Görseller için kendi alan adı, data URI’lar ve tüm HTTPS kaynakları
  • frame-ancestors ‘none’: Hiçbir yerde iframe olarak kullanılamaz (X-Frame-Options’ın modern alternatifi)
  • form-action ‘self’: Form verileri sadece kendi alan adına gönderilebilir

CSP’yi önce Content-Security-Policy-Report-Only moduyla test etmenizi şiddetle tavsiye ederim. Bu mod başlığı uygulamaz, sadece ihlalleri raporlar:

# Test modu - sadece raporlar, engellemez
Header always set Content-Security-Policy-Report-Only "default-src 'self'; report-uri /csp-report-endpoint"

Referrer-Policy

Kullanıcılar sitenizden başka bir siteye geçtiklerinde tarayıcının ne kadar bilgi paylaşacağını kontrol eder.

Header always set Referrer-Policy "strict-origin-when-cross-origin"
  • no-referrer: Hiç referrer bilgisi gönderme
  • strict-origin-when-cross-origin: Aynı origin için tam URL, cross-origin için sadece origin
  • same-origin: Sadece aynı origin için referrer gönder
  • no-referrer-when-downgrade: HTTPS’den HTTP’ye geçişte referrer gönderme

Permissions-Policy

Tarayıcı API’lerine ve donanım özelliklerine erişimi kontrol eder. Eski adı Feature-Policy‘dir.

Header always set Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), accelerometer=(), gyroscope=()"

Bu başlık sitenizin kamera, mikrofon, konum servisleri gibi hassas API’lere erişimini engeller. Eğer sitenizin bu özelliklere ihtiyacı yoksa tamamen kapatmak en güvenli seçenektir.

Gerçek Dünya Virtual Host Konfigürasyonu

Şimdi tüm bu başlıkları birleştirelim. Production ortamında kullandığım tipik bir yapıyı paylaşıyorum:

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    DocumentRoot /var/www/example

    # SSL konfigürasyonu buraya gelir
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    # Güvenlik başlıkları bloğu
    <IfModule mod_headers.c>
        # Clickjacking koruması
        Header always set X-Frame-Options "SAMEORIGIN"
        
        # MIME sniffing koruması
        Header always set X-Content-Type-Options "nosniff"
        
        # HSTS - 1 yıl
        Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
        
        # XSS koruması (eski tarayıcılar için)
        Header always set X-XSS-Protection "1; mode=block"
        
        # Referrer politikası
        Header always set Referrer-Policy "strict-origin-when-cross-origin"
        
        # İzin politikası
        Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
        
        # Content Security Policy
        Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline' https://fonts.googleapis.com; font-src 'self' https://fonts.gstatic.com; img-src 'self' data: https:; connect-src 'self'; frame-ancestors 'self'; form-action 'self'"
        
        # Sunucu bilgisini gizle (ek güvenlik)
        Header always unset X-Powered-By
        Header always unset Server
    </IfModule>
</VirtualHost>

Başlıkları Koşullu Uygulamak

Her zaman tüm başlıkları her yere uygulamak istemeyebilirsiniz. Örneğin bir API endpoint’iniz varsa CSP gereksizdir. Veya belirli bir dizin için farklı kurallar gerekiyordur.

# Sadece /api/ yolu için başlık ekle
<Location /api/>
    Header always set Access-Control-Allow-Origin "https://app.example.com"
    Header always set Access-Control-Allow-Methods "GET, POST, OPTIONS"
    Header always set Access-Control-Allow-Headers "Content-Type, Authorization"
</Location>

# /static/ dizini için farklı cache başlıkları ve daha gevşek CSP
<Location /static/>
    Header always set Cache-Control "public, max-age=31536000, immutable"
</Location>

# Sadece belirli dosya tipleri için başlık ekle
<FilesMatch ".(js|css)$">
    Header always set X-Content-Type-Options "nosniff"
</FilesMatch>

.htaccess ile Başlık Yönetimi

Sunucu konfigürasyonuna erişiminiz yoksa veya paylaşımlı hosting kullanıyorsanız .htaccess dosyası üzerinden de başlıkları yönetebilirsiniz. Bunun çalışması için AllowOverride All veya en azından AllowOverride FileInfo direktifinin aktif olması gerekir.

# .htaccess dosyası
<IfModule mod_headers.c>
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-Content-Type-Options "nosniff"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set X-XSS-Protection "1; mode=block"
    
    # Güvenli olmayan bağlantıları reddet
    Header always set Strict-Transport-Security "max-age=31536000" env=HTTPS
</IfModule>

env=HTTPS koşulu önemli: HSTS başlığını sadece HTTPS bağlantılarda gönderir. HTTP üzerinden HSTS göndermek anlamsızdır çünkü tarayıcı bunu zaten güvensiz kanaldan almış olur.

Başlıkları Test Etmek

Konfigürasyonu yaptıktan sonra mutlaka test etmelisiniz. Birkaç yöntem var:

# curl ile başlıkları kontrol et
curl -I https://example.com

# Sadece güvenlik başlıklarını göster
curl -I https://example.com 2>/dev/null | grep -iE "(strict-transport|x-frame|x-content|content-security|referrer|permissions)"

# Yönlendirmeleri takip ederek test et
curl -IL https://example.com

# Belirli bir başlık var mı kontrol et
curl -s -o /dev/null -D - https://example.com | grep -i "content-security-policy"

Online araçlar da oldukça kullanışlı:

  • securityheaders.com: Sitenizin başlıklarını puanlayan ve eksikleri gösteren ücretsiz araç
  • observatory.mozilla.org: Mozilla’nın güvenlik test aracı
  • ssllabs.com: SSL/TLS yapılandırmasıyla birlikte başlıkları da test eder

Sık Karşılaşılan Sorunlar ve Çözümleri

Başlık ikiye katlanıyor: Uygulama katmanı da aynı başlığı gönderiyorsa Apache ile çakışma olabilir. Bu durumda Header set yerine Header always set kullanın veya uygulamanın başlığını kaldırın.

# Tekrar eden başlıkları tespit et
curl -I https://example.com | sort | uniq -d

CSP sonrası bazı kaynaklar yüklenmiyor: Tarayıcının geliştirici konsolunda CSP ihlal mesajlarını görebilirsiniz. Önce Content-Security-Policy-Report-Only kullanarak hangi kaynakların engellendiğini tespit edin.

# Sorunlu direktifi geçici olarak gevşet
Header always set Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval';"

HSTS sonrası siteye erişilemiyor: Eğer bir alt alan adı HTTPS’yi desteklemiyorsa includeSubDomains o alan adını erişilemez kılar. Çözüm için önce tüm alt alan adlarında HTTPS’yi aktif edin, ardından başlığı ekleyin.

Eski tarayıcı uyumluluk sorunları: Bazı eski başlıklar artık kullanılmıyor. X-XSS-Protection artık modern tarayıcılarda desteklenmiyor ama zarar da vermiyor. X-Powered-By başlığını gizlemek güvenlik açısından iyi bir pratik olmaya devam ediyor.

Başlıkları Merkezi Olarak Yönetmek

Birden fazla virtual host’unuz varsa aynı başlıkları her birinde tekrar tekrar yazmak yerine merkezi bir konfigürasyon dosyası kullanabilirsiniz:

# /etc/apache2/conf-available/security-headers.conf dosyası oluştur
sudo nano /etc/apache2/conf-available/security-headers.conf
# /etc/apache2/conf-available/security-headers.conf
<IfModule mod_headers.c>
    Header always set X-Frame-Options "SAMEORIGIN"
    Header always set X-Content-Type-Options "nosniff"
    Header always set Referrer-Policy "strict-origin-when-cross-origin"
    Header always set Permissions-Policy "camera=(), microphone=(), geolocation=()"
    
    # HSTS sadece HTTPS için
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" env=HTTPS
    
    # Sunucu bilgilerini gizle
    Header always unset X-Powered-By
</IfModule>
# Konfigürasyonu etkinleştir
sudo a2enconf security-headers

# Sözdizimi kontrolü
sudo apache2ctl configtest

# Yeniden yükle
sudo systemctl reload apache2

Bu yaklaşımla tek bir dosyayı güncelleyerek tüm sanal sunucularınıza değişikliği yayabilirsiniz.

Güvenlik Taraması Sonrası İyileştirme Süreci

Gerçek bir senaryo: Bir müşterinin sitesini securityheaders.com ile taradım ve F notu aldı. Hiç güvenlik başlığı yoktu. Adım adım ne yaptık:

Önce mevcut durumu belgeledik:

# Mevcut başlıkları kaydet
curl -I https://musteri-site.com > /tmp/before-headers.txt

# Uygulamanın zaten hangi başlıkları gönderdiğini tespit et
curl -I https://musteri-site.com | grep -v "^HTTP|^Date|^Server|^Content|^Transfer|^Connection|^Vary"

Ardından staging ortamında Report-Only moduyla CSP’yi test ettik. İki gün boyunca konsol loglarını izleyerek hangi harici kaynakların kullanıldığını tespit ettik ve CSP politikasını buna göre oluşturduk. Production’a aldıktan sonra site A+ notunu aldı ve herhangi bir kullanıcı şikayeti gelmedi.

Bu süreç önemli bir dersi veriyor: Özellikle CSP’yi aceleyle production’a almayın. Mutlaka test edin.

Sonuç

mod_headers ile HTTP güvenlik başlıklarını yönetmek, web sunucusu güvenliğinin vazgeçilmez bir parçasıdır. Doğru yapılandırılmış güvenlik başlıkları; XSS, clickjacking, MIME sniffing ve benzeri saldırılara karşı tarayıcı katmanında güçlü bir savunma hattı oluşturur.

Özetleyecek olursam, öncelikle X-Frame-Options, X-Content-Type-Options ve Strict-Transport-Security başlıklarıyla başlayın. Bunlar en az riskli ve en yüksek faydalı başlıklardır. Ardından Referrer-Policy ve Permissions-Policy ekleyin. CSP’yi ise dikkatli bir test sürecinin ardından production’a alın.

Her değişiklikten sonra curl -I ile kontrol edin, securityheaders.com gibi araçlarla puanlamanızı takip edin ve özellikle CSP ihlallerini monitoring sisteminize dahil edin. Güvenlik sürekli bir süreçtir; bir kere yapıp unutmak değil, düzenli olarak gözden geçirip güncellemek gerekir.

Yorum yapın