Web sunucunuzu yapılandırırken güvenlik duvarı kurmak, SSL sertifikası almak gibi adımları atıyorsunuz ama çoğu zaman HTTP güvenlik başlıklarını atlıyorsunuz. Oysa bu başlıklar, XSS saldırılarından clickjacking’e, MIME sniffing’den bilgi sızıntısına kadar onlarca farklı saldırı vektörüne karşı ilk savunma hattınızı oluşturuyor. Nginx üzerinde doğru yapılandırılmış güvenlik başlıkları ve CSP politikası, web uygulamanızın güvenlik skorunu dramatik biçimde artırıyor. Bu yazıda hem teorik altyapıyı hem de production ortamında kullanabileceğiniz gerçek konfigürasyonları ele alacağız.
HTTP Güvenlik Başlıkları Neden Bu Kadar Önemli?
Bir web sunucusu sadece içerik serve etmekle kalmaz, aynı zamanda tarayıcıya bu içerikle nasıl davranması gerektiğini söyler. HTTP response başlıkları tam olarak bu işi yapar. Güvenlik başlıkları ise tarayıcıya belirli güvenlik politikalarını uygulama talimatı verir.
Düşünün: Bir kullanıcı sitenize giriyor, bir saldırgan da o kullanıcının tarayıcısında JavaScript çalıştırmaya çalışıyor. Eğer doğru CSP başlığını eklemediyseniz, tarayıcının bu kötü amaçlı kodu çalıştırmasını engelleyecek hiçbir mekanizmanız yok. Ya da siteniz iframe içinde başka bir sitede gösteriliyor ve bir clickjacking saldırısına zemin hazırlanıyor. X-Frame-Options başlığı olmadan bunu engelleyemezsiniz.
SecurityHeaders.com veya Mozilla Observatory gibi araçlarla sitenizi test ettiğinizde F notu alıyorsanız, bu yazı tam size göre.
Temel Güvenlik Başlıkları
Önce her başlığın ne işe yaradığını anlayalım, sonra Nginx’te nasıl yapılandıracağımıza geçelim.
Strict-Transport-Security (HSTS): Tarayıcıya “bu siteyle sadece HTTPS üzerinden konuş” talimatı verir. Bir kez bu başlığı aldıktan sonra tarayıcı, belirlenen süre boyunca HTTP isteklerini otomatik olarak HTTPS’e yönlendirir.
X-Frame-Options: Sitenizin iframe içinde gösterilip gösterilemeyeceğini kontrol eder. Clickjacking saldırılarına karşı koruma sağlar.
X-Content-Type-Options: Tarayıcıların MIME sniffing yapmasını engeller. Yani tarayıcı, sunucunun söylediği content type’ı kabul eder, kendi tahminini yapmaz.
Referrer-Policy: Kullanıcı sitenizden başka bir siteye geçtiğinde ne kadar referrer bilgisi gönderileceğini kontrol eder.
Permissions-Policy: Tarayıcı API’larına (kamera, mikrofon, konum vs.) erişim izinlerini yönetir.
X-XSS-Protection: Eski tarayıcılar için XSS filtresi. Modern tarayıcılarda bu görev CSP’ye devredilmiş olsa da geriye dönük uyumluluk için hâlâ önemli.
Nginx’te Temel Güvenlik Başlıklarını Ekleme
Nginx’te güvenlik başlıklarını eklemek için add_header direktifini kullanıyoruz. En iyi pratik, bu başlıkları merkezi bir dosyada tanımlayıp tüm virtual host konfigürasyonlarına dahil etmek.
Önce merkezi bir güvenlik başlıkları dosyası oluşturalım:
sudo nano /etc/nginx/conf.d/security-headers.conf
# Temel güvenlik başlıkları
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=()" always;
# Sunucu bilgisini gizle
server_tokens off;
Bu dosyayı her virtual host bloğuna dahil etmek için:
server {
listen 443 ssl;
server_name example.com;
# Güvenlik başlıklarını dahil et
include /etc/nginx/conf.d/security-headers.conf;
# ... diğer konfigürasyonlar
}
Burada dikkat edilmesi gereken önemli bir nokta var: add_header direktifi Nginx’te inheritance (miras) konusunda biraz sinsi davranır. Eğer bir location bloğunda add_header kullanırsanız, üst blokta tanımlı başlıklar o location için geçersiz hale gelir. Bu yüzden always parametresini kullanmak ve başlıkları doğru katmanda tanımlamak kritik önem taşıyor.
Content Security Policy (CSP) Derinlemesine
CSP, modern web güvenliğinin belki de en güçlü ama aynı zamanda en karmaşık başlığı. Tarayıcıya hangi kaynakların güvenilir olduğunu söylüyor ve bu kaynaklar dışından hiçbir şeyin yüklenmesine izin vermiyor.
CSP Direktifleri
CSP başlığı birden fazla direktiften oluşur. En önemli direktifler şunlar:
- default-src: Diğer direktifler için varsayılan kaynak politikası
- script-src: JavaScript dosyaları için izin verilen kaynaklar
- style-src: CSS dosyaları için izin verilen kaynaklar
- img-src: Resimler için izin verilen kaynaklar
- font-src: Web fontları için izin verilen kaynaklar
- connect-src: XHR, fetch, WebSocket bağlantıları için izin verilen kaynaklar
- frame-src: iframe içeriği için izin verilen kaynaklar
- object-src: Plugin içerikleri (Flash vs.) için politika
- base-uri:
tag’inin kullanabileceği URL’ler - form-action: Form gönderimlerinin yapılabileceği URL’ler
- upgrade-insecure-requests: HTTP isteklerini otomatik HTTPS’e yükselt
Basit Bir Site için CSP
Diyelim ki sadece kendi domain’inizden kaynaklar yükleyen, Google Fonts kullanan ve Google Analytics entegrasyonu olan bir site yönetiyorsunuz:
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' https://www.googletagmanager.com https://www.google-analytics.com;
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https://www.google-analytics.com;
connect-src 'self' https://www.google-analytics.com;
object-src 'none';
base-uri 'self';
form-action 'self';
upgrade-insecure-requests;
" always;
CSP Report-Only Modu: Production’da Güvenli Test
Direkt kısıtlayıcı CSP eklemek, özellikle eski ve büyük sitelerde işleri bozabilir. Bu yüzden Content-Security-Policy-Report-Only başlığını kullanarak önce test etmek çok akıllıca bir yaklaşım.
add_header Content-Security-Policy-Report-Only "
default-src 'self';
script-src 'self' https://www.googletagmanager.com;
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data:;
object-src 'none';
report-uri /csp-report-endpoint;
" always;
report-uri direktifi ile ihlalleri bir endpoint’e raporlayabilirsiniz. Bu sayede hangi kaynakların blokladığını görebilir, CSP’nizi production’ı kırmadan ince ayar yapabilirsiniz.
Gerçek Dünya Senaryoları
Senaryo 1: WordPress Sitesi
WordPress siteleri plugin’ler, tema dosyaları ve çeşitli üçüncü parti entegrasyonlar nedeniyle CSP yazmanın en zorlu olduğu ortamlardan biri. İşte makul güvenlik sağlayan ama WordPress’i kırmayan bir başlangıç noktası:
# /etc/nginx/sites-available/wordpress.conf
server {
listen 443 ssl http2;
server_name myblog.com www.myblog.com;
# SSL konfigürasyonu
ssl_certificate /etc/letsencrypt/live/myblog.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/myblog.com/privkey.pem;
# Temel güvenlik başlıkları
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# WordPress için daha permissive CSP (başlangıç noktası)
add_header Content-Security-Policy-Report-Only "
default-src 'self';
script-src 'self' 'unsafe-inline' 'unsafe-eval' https://www.googletagmanager.com https://www.google-analytics.com;
style-src 'self' 'unsafe-inline' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com data:;
img-src 'self' data: https: blob:;
connect-src 'self' https://www.google-analytics.com;
object-src 'none';
report-uri https://myblog.com/wp-json/csp/v1/report;
" always;
server_tokens off;
root /var/www/wordpress;
index index.php;
# ... location blokları
}
WordPress için ‘unsafe-inline’ ve ‘unsafe-eval’ kullanmak zorunda kalabilirsiniz çünkü birçok plugin inline script kullanıyor. Ancak bu zaman içinde nonce tabanlı yaklaşıma geçiş yapmanın hedeflendiği bir geçiş noktası olmalı.
Senaryo 2: React/Vue SPA Uygulaması
Modern JavaScript uygulamaları genellikle daha temiz bir CSP yazma imkânı sunar:
server {
listen 443 ssl http2;
server_name app.example.com;
ssl_certificate /etc/letsencrypt/live/app.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/app.example.com/privkey.pem;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer" always;
add_header Permissions-Policy "camera=(), microphone=(), geolocation=()" always;
# SPA için sıkı CSP
add_header Content-Security-Policy "
default-src 'none';
script-src 'self';
style-src 'self';
img-src 'self' data: blob:;
font-src 'self';
connect-src 'self' https://api.example.com;
worker-src 'self' blob:;
manifest-src 'self';
base-uri 'none';
form-action 'self';
frame-ancestors 'none';
upgrade-insecure-requests;
" always;
server_tokens off;
root /var/www/app/dist;
index index.html;
location / {
try_files $uri $uri/ /index.html;
}
}
Bu yapılandırma oldukça sıkı: default-src 'none' ile başlayıp sadece ihtiyaç duyulan kaynaklara izin veriyoruz. SPA’lar için bu yaklaşım ideal çünkü tüm kaynaklarınız bundle sırasında biliniyor.
Nginx’te Sunucu Bilgisini Gizleme
Güvenlik başlıklarına ek olarak, sunucunuzun ne olduğunu belli eden bilgileri de gizlemeniz önemli. Saldırganlar Nginx versiyon numarasını bilirse, o versiyona özel açıkları araştırabilir.
# /etc/nginx/nginx.conf içinde http bloğunda
http {
server_tokens off;
# Daha ileri gitmek isteyenler için
# nginx-extras veya openresty ile:
more_clear_headers Server;
more_set_headers "Server: WebServer";
}
Standart Nginx’te server_tokens off Server başlığından versiyon numarasını kaldırır ama “nginx” yazısını bırakır. Tamamen özelleştirmek için nginx-extras paketini kurmanız gerekiyor:
sudo apt install nginx-extras
# Ardından nginx.conf'a ekleyin:
more_set_headers "Server: ";
Konfigürasyonu Test Etme
Yaptığınız değişiklikleri uygulamadan önce mutlaka test edin:
# Syntax kontrolü
sudo nginx -t
# Başarılıysa reload
sudo systemctl reload nginx
# Başlıkları curl ile kontrol et
curl -I https://example.com
# Belirli başlıkları filtrele
curl -sI https://example.com | grep -i "security|content-security|x-frame|strict"
Daha kapsamlı test için:
# Mozilla Observatory CLI (npm ile kurulabilir)
npx observatory-cli example.com
# Veya securityheaders.com'u curl ile kullan
curl "https://securityheaders.com/?q=example.com&followRedirects=on" -s | grep "Score"
Dikkat Edilmesi Gereken Yaygın Hatalar
HSTS preload konusunda acele etmeyin: preload direktifini eklemek, sitenizi tarayıcıların hardcoded HSTS listesine ekleme başvurusu yapmanıza olanak tanır. Ama bu geri alınamaz bir işlem. Önce max-age=300 gibi kısa bir süreyle başlayın, her şeyin çalıştığından emin olun, sonra artırın.
always parametresini unutmayın: Nginx’te add_header direktifi varsayılan olarak sadece 200, 201, 204, 206, 301, 302, 303, 304, 307, 308 yanıt kodları için başlık ekler. always parametresi olmadan hata sayfalarında başlıklarınız eksik olacak.
Location bloklarında dikkatli olun: Daha önce de belirttiğim gibi, bir location bloğunda add_header kullanırsanız üst bloktan gelen başlıklar devre dışı kalır. Bunu çözmek için nginx-headers-more modülünü ya da her location bloğunda başlıkları tekrar tanımlamayı kullanabilirsiniz.
CSP’yi aşamalı olarak sıkılaştırın: Bir günde mükemmel CSP yazmaya çalışmayın. Report-Only modunda başlayın, logları inceleyin, gerçekten neye ihtiyaç duyduğunuzu anlayın, sonra kısıtlayıcı moda geçin.
Tüm Parçaları Bir Araya Getirme
İşte production ortamı için kullanabileceğiniz kapsamlı bir Nginx konfigürasyonu:
# /etc/nginx/conf.d/security.conf
# Tüm site genelinde geçerli olacak güvenlik ayarları
# Nginx versiyon bilgisini gizle
server_tokens off;
# SSL/TLS Güvenlik Ayarları
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384;
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;
# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;
# /etc/nginx/sites-available/secure-site.conf
server {
listen 80;
server_name example.com www.example.com;
# HTTP'yi HTTPS'e yönlendir
return 301 https://$server_name$request_uri;
}
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
# HSTS - önce kısa süre, test et, sonra artır
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# Clickjacking koruması
add_header X-Frame-Options "SAMEORIGIN" always;
# MIME sniffing koruması
add_header X-Content-Type-Options "nosniff" always;
# XSS filtresi (eski tarayıcılar için)
add_header X-XSS-Protection "1; mode=block" always;
# Referrer politikası
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# İzin politikası
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=()" always;
# CSP - kendi ihtiyaçlarınıza göre düzenleyin
add_header Content-Security-Policy "
default-src 'self';
script-src 'self' https://www.googletagmanager.com;
style-src 'self' https://fonts.googleapis.com;
font-src 'self' https://fonts.gstatic.com;
img-src 'self' data: https:;
connect-src 'self';
object-src 'none';
base-uri 'self';
form-action 'self';
frame-ancestors 'self';
upgrade-insecure-requests;
" always;
root /var/www/example;
index index.html index.php;
# Gizli dosya ve dizinlere erişimi engelle
location ~ /. {
deny all;
access_log off;
log_not_found off;
}
# Yedek ve geçici dosyalara erişimi engelle
location ~* .(bak|config|sql|fla|psd|ini|log|sh|inc|swp|dist)$ {
deny all;
}
location / {
try_files $uri $uri/ =404;
}
}
Sonuç
Nginx güvenlik başlıkları ve CSP politikası, web güvenliğinin en hızlı kazanım sağlayan alanlarından biri. Birkaç satır konfigürasyonla XSS, clickjacking, MIME sniffing ve bilgi sızıntısı gibi yaygın saldırı vektörlerine karşı güçlü bir savunma kurabiliyorsunuz.
En önemli tavsiyem şu: Her şeyi bir anda yapmaya çalışmayın. Önce server_tokens off ve temel başlıklarla başlayın. Sonra CSP’yi Report-Only modunda test edin. Logları inceleyin, ihlalleri düzeltin. Üç dört hafta içinde mükemmel bir güvenlik profiline ulaşabilirsiniz.
Konfigürasyonunuzu düzenli olarak SecurityHeaders.com ve Mozilla Observatory üzerinde test edin. Bu araçlar hem mevcut durumunuzu puan olarak gösterir hem de iyileştirme önerileri sunar. A+ hedeflemeniz gerekmiyor, ama en azından C’nin altında kalmamanızı öneririm.
Son olarak, CSP özellikle dinamik içerikli sitelerde sürekli bakım gerektiriyor. Yeni bir JavaScript kütüphanesi ekledikçe, yeni bir üçüncü parti servis entegre ettikçe CSP’nizi güncellemeniz gerekecek. Bunu bir yük olarak değil, sitenizin bağımlılıklarını disiplinli biçimde yönetmenin bir parçası olarak görün.