HSTS Nedir ve Nasıl Yapılandırılır?

Web sitenize bir kullanıcı HTTP üzerinden bağlandığında, tarayıcı ile sunucu arasındaki bu ilk握手tamamen açık metin olarak gerçekleşir. Siz sunucu tarafında HTTPS’e yönlendirme yapıyor olsanız bile, o ilk istek bir saldırgan tarafından ele geçirilebilir ve kullanıcı farkında olmadan HTTP üzerinde kalmaya devam edebilir. İşte HSTS (HTTP Strict Transport Security), tam olarak bu açığı kapatan bir güvenlik mekanizmasıdır. Bugün HSTS’nin ne olduğunu, nasıl çalıştığını ve farklı web sunucularında nasıl yapılandırıldığını ele alacağız.

HSTS Nedir?

HSTS, bir web sunucusunun tarayıcıya “benimle sadece HTTPS üzerinden konuş” dediği bir HTTP güvenlik başlığıdır. RFC 6797 ile standartlaştırılan bu mekanizma, tarayıcının belirli bir süre boyunca ilgili domain için yalnızca HTTPS bağlantısı kullanmasını zorunlu kılar.

Teknik açıdan baktığımızda, sunucu HTTPS yanıtına şu başlığı ekler:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Tarayıcı bu başlığı gördükten sonra, belirlenen süre boyunca (max-age saniye cinsinden) o domain’e yapılan tüm HTTP isteklerini otomatik olarak HTTPS’e çevirir. Sunucuya bile sormadan, tamamen istemci tarafında bu dönüşüm gerçekleşir.

SSL Stripping Saldırısı ve HSTS’nin Önemi

HSTS’yi neden kullandığımızı anlamak için önce SSL Stripping saldırısını kavramak gerekiyor.

Saldırgan, kullanıcı ile sunucu arasına girerek (Man-in-the-Middle) şu senaryoyu uygular:

  • Kullanıcı http://bank.example.com adresine bağlanmaya çalışır
  • Saldırgan bu isteği yakalar ve sunucuyla kendi HTTPS bağlantısını kurar
  • Sunucudan aldığı HTTPS yanıtını HTTP’ye çevirerek kullanıcıya iletir
  • Kullanıcı HTTP üzerinden iletişim kurduğunu fark etmez, tüm veriler açıkta kalır

Sunucu tarafında 301/302 yönlendirme yapılmış olsa bile, bu ilk HTTP isteği saldırgan tarafından yakalanabilir. HSTS ile tarayıcı, HTTP isteğini sunucuya hiç göndermez ve doğrudan HTTPS kullanır. Saldırganın araya girebileceği ilk HTTP isteği ortadan kalkar.

HSTS Başlık Parametreleri

max-age

max-age: Tarayıcının bu kuralı kaç saniye boyunca hatırlayacağını belirtir. Yaygın değerler:

  • max-age=3600: 1 saat (test ortamları için)
  • max-age=86400: 1 gün (geçiş dönemlerinde)
  • max-age=2592000: 30 gün
  • max-age=31536000: 1 yıl (üretim ortamları için önerilen)
  • max-age=63072000: 2 yıl (preload listesi için minimum)

includeSubDomains

includeSubDomains: Bu direktif eklendiğinde, kural yalnızca ana domain için değil, tüm alt domainler için de geçerli olur. api.example.com, mail.example.com, cdn.example.com gibi tüm subdomainler HTTPS zorunluluğuna tabi olur. Bu direktifi eklemeden önce tüm subdomainlerinizin geçerli SSL sertifikasına sahip olduğundan emin olun.

preload

preload: Tarayıcıların içine gömülü olan HSTS Preload listesine dahil olmak istediğinizi belirtir. Bu liste sayesinde, kullanıcı sitenize ilk kez hiç girmeden bile tarayıcı HTTPS kullanır. Sadece bu direktifi eklemek yeterli değildir; ayrıca hstspreload.org üzerinden başvuru yapmanız gerekir.

Nginx’te HSTS Yapılandırması

Nginx için en temel HSTS yapılandırması şu şekildedir:

# /etc/nginx/sites-available/example.com

server {
    listen 80;
    server_name example.com www.example.com;
    
    # HTTP'den HTTPS'e kalıcı yönlendirme
    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 başlığı - sadece HTTPS bloğuna eklenmeli
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
    
    # Diğer güvenlik başlıkları
    add_header X-Content-Type-Options "nosniff" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    
    location / {
        root /var/www/example.com;
        index index.html;
    }
}

Önemli bir nokta: add_header direktifini sadece HTTPS server bloğuna ekleyin. HTTP bloğuna eklerseniz, tarayıcı HTTP üzerinden HSTS başlığı alır ve bu güvenilir değildir.

Yapılandırmayı test edip yeniden yükleyelim:

# Nginx yapılandırmasını test et
nginx -t

# Başarılıysa servisi yeniden yükle
systemctl reload nginx

# HSTS başlığını doğrula
curl -I https://example.com | grep -i strict

Nginx’te Global HSTS Yapılandırması

Birden fazla siteniz varsa, her site için tekrar tekrar yazmak yerine global bir yapılandırma dosyası oluşturabilirsiniz:

# /etc/nginx/conf.d/security-headers.conf
# Bu dosyayı HTTPS server bloklarınızda include edin

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Sonra her HTTPS server bloğunda:

server {
    listen 443 ssl http2;
    server_name example.com;
    
    include /etc/nginx/conf.d/security-headers.conf;
    
    # Diğer yapılandırmalar...
}

Apache’de HSTS Yapılandırması

Apache için önce mod_headers modülünün etkin olduğundan emin olun:

# mod_headers'ı etkinleştir
a2enmod headers
a2enmod rewrite

# Servisi yeniden başlat
systemctl restart apache2

Ardından virtual host yapılandırmasına HSTS ekleyin:

# /etc/apache2/sites-available/example.com.conf

<VirtualHost *:80>
    ServerName example.com
    ServerAlias www.example.com
    
    # HTTP'yi HTTPS'e yönlendir
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

<VirtualHost *:443>
    ServerName example.com
    ServerAlias www.example.com
    
    SSLEngine on
    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
    
    # HSTS başlığını sadece HTTPS bağlantılarına ekle
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    
    DocumentRoot /var/www/example.com
    
    <Directory /var/www/example.com>
        AllowOverride All
        Require all granted
    </Directory>
</VirtualHost>

Apache’de .htaccess dosyası üzerinden de yapılandırma yapılabilir, ancak bu yöntem performans açısından tercih edilmez. Yine de paylaşımlı hosting gibi durumlarda işe yarar:

# .htaccess
<IfModule mod_rewrite.c>
    RewriteEngine On
    RewriteCond %{HTTPS} off
    RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</IfModule>

<IfModule mod_headers.c>
    # Sadece HTTPS bağlantılarda başlık ekle
    Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" env=HTTPS
</IfModule>

HAProxy ile HSTS

Load balancer olarak HAProxy kullanıyorsanız, HSTS’yi backend sunuculara bırakmak yerine HAProxy seviyesinde merkezi olarak yönetmek çok daha pratiktir:

# /etc/haproxy/haproxy.cfg

frontend https_frontend
    bind *:443 ssl crt /etc/haproxy/certs/example.com.pem
    bind *:80
    
    # HTTP'yi HTTPS'e yönlendir
    http-request redirect scheme https unless { ssl_fc }
    
    # HSTS başlığını frontend'de ekle
    http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    
    default_backend web_servers

backend web_servers
    balance roundrobin
    server web1 192.168.1.10:80 check
    server web2 192.168.1.11:80 check

Uygulama Seviyesinde HSTS

Node.js / Express

# npm install helmet
const express = require('express');
const helmet = require('helmet');
const app = express();

// Helmet ile HSTS yapılandırması
app.use(helmet.hsts({
    maxAge: 31536000,
    includeSubDomains: true,
    preload: true
}));

// Ya da manuel olarak
app.use((req, res, next) => {
    if (req.secure) {
        res.setHeader(
            'Strict-Transport-Security',
            'max-age=31536000; includeSubDomains; preload'
        );
    }
    next();
});

Python / Django

# settings.py

SECURE_HSTS_SECONDS = 31536000
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
SECURE_SSL_REDIRECT = True

# Nginx/Apache arkasında çalışıyorsa
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

HSTS Preload Listesine Başvuru

Preload listesi, Chrome, Firefox, Safari, Edge gibi tarayıcılara gömülü olarak gelir ve bu listedeki domainler için tarayıcı ilk ziyaretten itibaren HTTPS kullanır.

Başvuru için gereksinimler:

  • Geçerli bir SSL sertifikasına sahip olmak
  • HTTP’den HTTPS’e yönlendirme yapıyor olmak
  • Tüm subdomainlerin HTTPS desteklemesi
  • max-age değerinin en az 31536000 (1 yıl) olması
  • includeSubDomains direktifinin bulunması
  • preload direktifinin bulunması

Başvuruyu https://hstspreload.org adresinden yapabilirsiniz. Listeye dahil olmak birkaç ay sürebilir ve bir sonraki tarayıcı güncellemesiyle birlikte etkili olur.

Dikkat: Preload listesinden çıkmak, listeye girmekten çok daha zor ve uzun sürelidir. Bu kararı vermeden önce tüm subdomainlerinizin kalıcı olarak HTTPS’i destekleyeceğinden emin olun.

Gerçek Dünya Senaryoları

Senaryo 1: Kademeli HSTS Geçişi

Büyük bir e-ticaret sitesini yönetiyorsunuz. Bazı subdomainlerinizin henüz SSL sertifikası yok ve hızlıca includeSubDomains eklemek sorun çıkarır. Kademeli geçiş şu şekilde olabilir:

İlk hafta, düşük max-age ile başlayın:

Strict-Transport-Security: max-age=86400

Bir hafta sonra sorun çıkmıyorsa artırın:

Strict-Transport-Security: max-age=604800

Tüm subdomainlere SSL ekledikten sonra:

Strict-Transport-Security: max-age=2592000; includeSubDomains

Kararlı bir süre sonra tam yapılandırmaya geçin:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Senaryo 2: HSTS’yi Sıfırlamak

Bir alt domain’in SSL sertifikasını yenilemeyi unuttuğunuzu ve HSTS nedeniyle kullanıcıların siteye erişemediğini varsayalım. Kısa vadeli çözüm olarak max-age=0 döndürebilirsiniz:

# Nginx - Acil HSTS sıfırlama
add_header Strict-Transport-Security "max-age=0" always;

Bu, tarayıcıların HSTS kaydını silmesini sağlar, ancak halihazırda etkilenen kullanıcılar için elle temizleme gerekebilir. Chrome’da chrome://net-internals/#hsts adresinden belirli bir domain için HSTS kaydı silinebilir.

Senaryo 3: Staging Ortamında HSTS

Staging ortamınızda HSTS etkinleştirirseniz ve bu ortam production ile aynı domain adını kullanıyorsa, geliştiricilerin tarayıcılarında kalıcı bir zorunluluk oluşturabilirsiniz. Staging için kısa max-age kullanın veya HSTS’yi tamamen devre dışı bırakın:

# staging.nginx.conf
server {
    listen 443 ssl;
    server_name staging.example.com;
    
    # Staging'de HSTS ekleme, kısa tutuyoruz
    add_header Strict-Transport-Security "max-age=3600" always;
    
    # Production benzeri konfigürasyon buraya...
}

HSTS Yapılandırmasını Test Etme

Yapılandırmanın doğru çalıştığını test etmek için birkaç yöntem:

# curl ile başlık kontrolü
curl -sI https://example.com | grep -i "strict-transport"

# Beklenen çıktı:
# strict-transport-security: max-age=31536000; includeSubDomains; preload

# OpenSSL ile SSL/TLS kontrolü
openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates

# Nmap ile HSTS kontrolü
nmap --script http-security-headers -p 443 example.com

# HTTPie ile daha okunaklı çıktı
http --headers https://example.com | grep -i strict

Online araçlar için securityheaders.com ve ssllabs.com/ssltest adreslerini kullanabilirsiniz. SSL Labs, HSTS yapılandırmanızı değerlendirerek A+ notu almanızı sağlar.

Sık Yapılan Hatalar

HSTS başlığını HTTP bloğuna eklemek: Tarayıcılar HSTS başlığını yalnızca güvenli bağlantılar üzerinden aldıklarında geçerli sayar. HTTP bloğuna eklenen HSTS başlığı görmezden gelinir.

Self-signed sertifika ile HSTS: HSTS yalnızca geçerli, güvenilen bir sertifika ile anlamlıdır. Self-signed sertifika kullanan bir sitede HSTS aktif etseydiniz, kullanıcılar sertifika uyarısını aşamaz ve siteye hiç erişemez hale gelirlerdi.

Sertifika süresini kaçırmak: HSTS aktifken sertifikanızın süresi dolarsa, kullanıcılar sitenize erişemez. Sertifika yenilemeyi otomatize edin:

# Let's Encrypt otomatik yenileme kontrolü
systemctl status certbot.timer

# Manuel test
certbot renew --dry-run

# Cron ile yedek yenileme
echo "0 3 * * * root certbot renew --quiet --post-hook 'systemctl reload nginx'" > /etc/cron.d/certbot-renew

includeSubDomains’i acele eklemek: Tüm subdomainlerinizi kontrol etmeden bu direktifi eklemeyin. Özellikle üçüncü parti servislerin subdomainleri (mail, ftp, legacy uygulamalar) sorun çıkarabilir.

Sonuç

HSTS, modern web güvenliğinin olmazsa olmaz parçalarından biridir. SSL Stripping saldırılarına karşı etkili bir savunma hattı oluşturur ve kullanıcılarınızın bağlantılarını pasif olarak korur. Yapılandırması görece basit olsa da, yanlış uygulandığında ciddi erişim sorunlarına yol açabilir.

Uygulamaya geçerken kademeli bir yaklaşım benimseyin: önce düşük max-age ile başlayın, altyapınızın HTTPS için tamamen hazır olduğunu doğrulayın, sonra değeri artırın ve son olarak includeSubDomains ile preload direktiflerini ekleyin. Sertifika yenileme sürecinizi otomatize etmeyi unutmayın; HSTS aktifken süresi dolmuş bir sertifika ciddi kesintilere neden olur.

Preload listesine başvurmak geri dönüşü olmayan bir adımdır. Bu kararı vermeden önce tüm altyapınızın uzun vadede HTTPS’i destekleyeceğinden emin olun. Doğru yapılandırılmış bir HSTS politikası, SSL Labs testlerinde A+ notunu almanızı sağlayacak ve kullanıcılarınızın güvenliğini önemli ölçüde artıracaktır.

Yorum yapın