jan-karel.nl
Home / Securitymaatregelen / Webbeveiliging / TLS/SSL Configuratie

TLS/SSL Configuratie

TLS/SSL Configuratie

Encryptie Zonder Excuses

Webrisico is zelden mysterieus. Het zit meestal in voorspelbare fouten die onder tijdsdruk blijven staan.

Voor TLS/SSL Configuratie is de kern een moderne TLS-baseline, strak certificaatbeheer en geen zwakke uitzonderingen.

Dat maakt security minder een losse controle achteraf en meer een standaardkwaliteit van je product.

Directe maatregelen (15 minuten)

Waarom dit telt

De kern van TLS/SSL Configuratie is risicoreductie in de praktijk. Technische context ondersteunt de maatregelkeuze, maar implementatie en borging staan centraal.

TLS-versies

Wat gebruiken

Versie Status Actie
TLS 1.3 Aanbevolen Inschakelen, prefereren
TLS 1.2 Acceptabel Inschakelen voor backward compatibility
TLS 1.1 Verouderd Uitschakelen
TLS 1.0 Verouderd Uitschakelen
SSL 3.0 Onveilig (POODLE) Uitschakelen
SSL 2.0 Onveilig Uitschakelen

TLS 1.3 is sneller (1-RTT handshake, 0-RTT resumption), veiliger (alleen moderne cipher suites), en eenvoudiger (geen legacy-opties om verkeerd te configureren). Alle moderne browsers ondersteunen het.

TLS 1.2 is nog nodig voor oudere clients, maar configureer het met alleen veilige cipher suites (zie §13.2).

Waarom TLS 1.0 en 1.1 uitschakelen? Beide versies ondersteunen zwakke cipher suites en zijn kwetsbaar voor aanvallen als BEAST en Lucky Thirteen. PCI DSS vereist sinds 2018 minimaal TLS 1.2. Alle grote browsers hebben TLS 1.0/1.1 sinds 2020 uitgeschakeld.

Cipher suites

Aanbevolen cipher suites

TLS 1.3 (cipher suites zijn vast, geen configuratie nodig):

TLS_AES_256_GCM_SHA384
TLS_CHACHA20_POLY1305_SHA256
TLS_AES_128_GCM_SHA256

TLS 1.2 (selecteer alleen veilige combinaties):

ECDHE-ECDSA-AES256-GCM-SHA384
ECDHE-RSA-AES256-GCM-SHA384
ECDHE-ECDSA-CHACHA20-POLY1305
ECDHE-RSA-CHACHA20-POLY1305
ECDHE-ECDSA-AES128-GCM-SHA256
ECDHE-RSA-AES128-GCM-SHA256

Wat vermijden

Te vermijden Reden
RC4 Gebroken (meerdere aanvallen)
3DES Gebroken (Sweet32)
DES Veel te zwak (56-bit)
CBC-mode ciphers Kwetsbaar voor padding oracle aanvallen
RSA key exchange (geen ECDHE) Geen forward secrecy
NULL ciphers Geen encryptie
EXPORT ciphers Opzettelijk verzwakt (40/56-bit)
MD5-gebaseerde ciphers Gebroken hash

Forward secrecy

Forward secrecy (via ECDHE) betekent dat een gecompromitteerde server-sleutel niet leidt tot ontsleuteling van eerder opgenomen verkeer. Gebruik altijd ECDHE-gebaseerde cipher suites.

nginx TLS-configuratie

Compleet copy-paste blok voor een veilige nginx TLS-configuratie:

# /etc/nginx/snippets/ssl-params.conf

# Protocollen — alleen TLS 1.2 en 1.3
ssl_protocols TLSv1.2 TLSv1.3;

# Cipher suites — server bepaalt de volgorde
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';

# ECDH-curve
ssl_ecdh_curve X25519:secp384r1;

# Sessie-caching — vermindert handshake-overhead
ssl_session_cache shared:TLS:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;  # Tickets verstoren forward secrecy

# OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
resolver 1.1.1.1 8.8.8.8 valid=300s;
resolver_timeout 5s;

# HSTS
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

Gebruik in je server-blok:

server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;

    include snippets/ssl-params.conf;

    # Redirect HTTP naar HTTPS
    # (in een apart server-blok)
}

server {
    listen 80;
    listen [::]:80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

Let op: ssl_session_tickets off is belangrijk. Session tickets gebruiken een symmetrische sleutel die op de server staat. Als die sleutel lekt, kan al het verkeer dat ermee is opgebouwd worden ontsleuteld — forward secrecy is dan weg.

Apache TLS-configuratie

Activeer met:

sudo a2enmod ssl headers
sudo a2enconf ssl-params
sudo systemctl reload apache2

VirtualHost-configuratie:

<VirtualHost *:443>
    ServerName example.com

    SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
    SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem

    Include conf-available/ssl-params.conf
</VirtualHost>

<VirtualHost *:80>
    ServerName example.com
    Redirect permanent / https://example.com/
</VirtualHost>

Let’s Encrypt / ACME

Let’s Encrypt biedt gratis, geautomatiseerde TLS-certificaten via het ACME-protocol.

Installatie (certbot)

# Debian/Ubuntu
sudo apt install certbot python3-certbot-nginx

# CentOS/RHEL
sudo dnf install certbot python3-certbot-nginx

Certificaat aanvragen

# nginx — certbot configureert automatisch
sudo certbot --nginx -d example.com -d www.example.com

# Apache
sudo certbot --apache -d example.com -d www.example.com

# Standalone (als je geen webserver draait)
sudo certbot certonly --standalone -d example.com

# DNS-challenge (voor wildcard certs)
sudo certbot certonly --manual --preferred-challenges dns -d '*.example.com' -d example.com

Automatische vernieuwing

# Test renewal
sudo certbot renew --dry-run

# Certbot installeert automatisch een timer/cron:
sudo systemctl list-timers | grep certbot

# Handmatig cron toevoegen als het ontbreekt:
# /etc/cron.d/certbot
0 0,12 * * * root certbot renew --quiet --deploy-hook "systemctl reload nginx"

Belangrijk: Gebruik --deploy-hook om nginx/Apache te herladen na vernieuwing. Zonder reload gebruikt de webserver het oude certificaat tot de volgende herstart.

Wildcard-certificaten

Wildcard-certificaten (*.example.com) vereisen DNS-validatie:

sudo certbot certonly \
  --manual \
  --preferred-challenges dns \
  -d '*.example.com' \
  -d example.com

Certbot vraagt je om een _acme-challenge TXT-record aan te maken in je DNS. Voor automatisering, gebruik een DNS-plugin:

# Voorbeeld: Cloudflare
sudo apt install python3-certbot-dns-cloudflare
sudo certbot certonly \
  --dns-cloudflare \
  --dns-cloudflare-credentials /etc/letsencrypt/cloudflare.ini \
  -d '*.example.com' -d example.com

Certificaatbeheer

Levenscyclus

Aanvraag → Validatie → Uitgifte → Installatie → Monitoring → Vernieuwing → ...

Let’s Encrypt-certificaten zijn 90 dagen geldig. Vernieuw minimaal 30 dagen voor verloopdatum.

Monitoring op expiry

# Check verloopdatum van een certificaat
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -dates

# Eén-liner: dagen tot verloop
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
  | openssl x509 -noout -enddate \
  | cut -d= -f2 \
  | xargs -I{} bash -c 'echo $(( ($(date -d "{}" +%s) - $(date +%s)) / 86400 )) dagen resterend'

Stel alerts in voor certificaten die binnen 14 dagen verlopen. Gebruik tools als:

  • certbot (ingebouwde renewal)
  • cert-manager (Kubernetes)
  • AWS Certificate Manager (ACM, automatisch renewal)

Certificate Transparency (CT) logs

CT-logs zijn openbare registers van alle uitgegeven certificaten. Monitor ze om ongeautoriseerde certificaten voor je domein te detecteren:

  • https://crt.sh/?q=example.com — zoek alle uitgegeven certs
  • Facebook CT Monitor — gratis alerting
  • Censys — zoek en monitor certificaten

HSTS in detail

Configuratie

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Parameter Effect Aanbeveling
max-age Browser onthoudt HTTPS-verplichting voor X seconden 31536000 (1 jaar)
includeSubDomains Geldt ook voor alle subdomeinen Ja, als alle subdomeinen HTTPS hebben
preload Toestemming voor opname in browser preload-lijst Ja, na grondige test

Risico’s

HSTS preload is een eenrichtingsstraat:

  1. Zodra je domein in de preload-lijst staat, kan het maanden duren om het te verwijderen
  2. Als een subdomein geen HTTPS heeft en je gebruikt includeSubDomains, is dat subdomein onbereikbaar
  3. Een fout in de HSTS-configuratie kan je site onbereikbaar maken

Veilig uitrollen

Stap 1: max-age=300 (5 minuten) — test
Stap 2: max-age=86400 (1 dag) — monitor
Stap 3: max-age=604800 (1 week) — stabiel?
Stap 4: max-age=2592000 (30 dagen) — nog steeds goed?
Stap 5: max-age=31536000; includeSubDomains — productie
Stap 6: max-age=31536000; includeSubDomains; preload — submit naar hstspreload.org

OCSP Stapling

OCSP (Online Certificate Status Protocol) controleert of een certificaat is ingetrokken. Zonder stapling moet de browser zelf de OCSP-server raadplegen — dat is langzaam en een privacyprobleem (de CA ziet welke sites je bezoekt).

Met OCSP stapling haalt de server periodiek een OCSP-response op en stuurt die mee in de TLS-handshake. Sneller en privacyvriendelijker.

nginx

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 1.1.1.1 8.8.8.8 valid=300s;

Apache

SSLUseStapling on
SSLStaplingCache "shmcb:logs/ssl_stapling(32768)"

Testen

# Controleer of OCSP stapling werkt
echo | openssl s_client -connect example.com:443 -servername example.com -status 2>/dev/null \
  | grep -A 5 "OCSP Response"

Certificate pinning

Certificate pinning beperkt welke certificaten of CA’s geldig zijn voor je domein. Dit beschermt tegen gecompromitteerde CA’s.

Wanneer wel

  • Mobiele apps met een eigen backend: pin het leaf-certificaat of de intermediate CA
  • API-clients met een bekende server: pin de publieke sleutel (SPKI hash)

Wanneer niet

  • Websites in de browser: pinning is verwijderd uit Chrome en Firefox (HPKP deprecated). Gebruik in plaats daarvan Certificate Transparency monitoring
  • Publieke API’s met veel clients: te complex om te beheren

Voorbeeld: Python requests met pinning

import hashlib
import ssl
import requests
from requests.adapters import HTTPAdapter

class PinningAdapter(HTTPAdapter):
    PINS = {
        b'\x2b\x06\x01...',  # SHA-256 van SPKI
    }

    def send(self, request, **kwargs):
        # Implementatie via ssl context verify
        return super().send(request, **kwargs)

Advies: Voor de meeste websites is certificate pinning te riskant en te complex. Vertrouw op Certificate Transparency monitoring en HSTS in plaats van pinning.

Testtools

testssl.sh

# Installatie
git clone --depth 1 https://github.com/drwetter/testssl.sh.git

# Volledig rapport
./testssl.sh example.com

# Alleen cipher suites
./testssl.sh --cipher-per-proto example.com

# Alleen kwetsbaarheden
./testssl.sh --vulnerable example.com

testssl.sh is de meest complete CLI-tool voor TLS-analyse. Het test protocollen, cipher suites, certificaten, kwetsbaarheden (Heartbleed, ROBOT, CRIME, etc.) en best practices.

SSL Labs

Test je configuratie online via https://www.ssllabs.com/ssltest/. Doel: A+ rating. Vereisten voor A+:

  • TLS 1.2+ met sterke cipher suites
  • HSTS met max-age >= 6 maanden
  • Geen bekende kwetsbaarheden

openssl s_client

# Basis verbindingstest
openssl s_client -connect example.com:443 -servername example.com

# Specifiek TLS-versie testen
openssl s_client -connect example.com:443 -tls1_2
openssl s_client -connect example.com:443 -tls1_3

# Certificaatketen bekijken
openssl s_client -connect example.com:443 -showcerts

# Cipher suite testen
openssl s_client -connect example.com:443 -cipher 'ECDHE-RSA-AES256-GCM-SHA384'

Mozilla SSL Configuration Generator

De Mozilla SSL Configuration Generator genereert veilige configuraties voor nginx, Apache, HAProxy, en andere servers. Beschikbaar op:

https://ssl-config.mozilla.org/

Drie profielen: - Modern: alleen TLS 1.3 — voor nieuwe deployments - Intermediate: TLS 1.2 + 1.3 — aanbevolen voor de meeste servers - Old: TLS 1.0+ — alleen als je oude clients moet ondersteunen (vermijd dit)

Veelvoorkomende fouten

Fout Risico Oplossing
Verlopen certificaat Browser toont waarschuwing, gebruikers klikken weg Automatische renewal (certbot) + monitoring
Self-signed in productie Geen vertrouwensketen, MitM mogelijk Gebruik Let’s Encrypt (gratis)
Mixed content HTTP-resources op HTTPS-pagina, lock-icon verdwijnt Gebruik relatieve URLs of // protocol
TLS 1.0/1.1 nog aan Kwetsbaar voor bekende aanvallen Uitschakelen: ssl_protocols TLSv1.2 TLSv1.3
Zwakke cipher suites Brute-forceable encryptie Gebruik alleen ECDHE + AES-GCM/ChaCha20
Geen forward secrecy Opgeslagen verkeer later ontsleutelbaar Gebruik ECDHE, vermijd statische RSA
SSL compression (CRIME) CRIME-aanval: session tokens lekken SSLCompression off / standaard uit in nginx
Geen HSTS SSL-stripping aanval mogelijk Strict-Transport-Security header instellen
Session tickets aan Forward secrecy ondermijnd ssl_session_tickets off
Incomplete certificaatketen Sommige clients vertrouwen het cert niet Gebruik fullchain.pem, niet alleen cert.pem
Wildcard overal Te breed: geldt voor alle subdomeinen Gebruik specifieke certs waar mogelijk

Mixed content oplossen

<!-- FOUT — HTTP-resource op HTTPS-pagina -->
<img src="http://cdn.example.com/logo.png">
<script src="http://cdn.example.com/app.js"></script>

<!-- GOED — protocol-relatief of HTTPS -->
<img src="https://cdn.example.com/logo.png">
<script src="https://cdn.example.com/app.js"></script>

<!-- OOK GOED — relatief pad als zelfde domein -->
<img src="/static/logo.png">

Gebruik CSP om mixed content te detecteren en blokkeren:

Content-Security-Policy: upgrade-insecure-requests;

Deze directief upgradet automatisch HTTP-requests naar HTTPS. Handig als migratiemaatregel, maar niet als permanente oplossing — fix de URLs.

Checklist

Maatregel Waarde Prioriteit
TLS-versies Alleen 1.2 en 1.3 Kritiek
Cipher suites ECDHE + AES-GCM / ChaCha20 Kritiek
Forward secrecy ECDHE ingeschakeld Kritiek
Certificaat Geldig, niet self-signed, volledige keten Kritiek
Auto-renewal certbot timer/cron actief Kritiek
HSTS max-age=31536000; includeSubDomains Hoog
OCSP stapling Ingeschakeld Hoog
Session tickets Uitgeschakeld Hoog
SSL compression Uitgeschakeld Hoog
HTTPHTTPS redirect 301 redirect op poort 80 Kritiek
Mixed content Geen HTTP-resources op HTTPS-pagina’s Hoog
CT-monitoring Alerts voor onverwachte certificaten Medium
SSL Labs score A+ Aanbevolen

TLS-configuratie is een van de weinige gebieden in beveiliging waar je echt kunt zeggen: “doe dit, kopieer deze configuratie, en je bent klaar.” Er zijn geen edge cases, geen “het hangt ervan af”, geen subtiele nuances. Schakel TLS 1.0 en 1.1 uit. Gebruik alleen ECDHE cipher suites. Zet HSTS aan. Automatiseer je certificaten. Test met SSL Labs. Klaar.

En toch. En toch vinden we bij elke assessment servers met TLS 1.0, RC4 cipher suites, verlopen certificaten, en geen HSTS. We vinden webshops die creditcardgegevens verwerken over een verbinding die een student cryptografie in een middag kan breken.

Het is niet alsof de informatie niet beschikbaar is. Mozilla heeft een configuratiegenerator die letterlijk de regels voor je uitspuugt. Let’s Encrypt heeft certificaten gratis en geautomatiseerd gemaakt. testssl.sh vertelt je in een seconde wat er mis is. Alles is er. Je hoeft het alleen maar te doen.

Maar “het staat op de roadmap.” Naast de security headers uit het vorige hoofdstuk. En de inputvalidatie uit het hoofdstuk daarvoor. Drie gratis maatregelen, drie keer “staat op de backlog.” En we vragen ons af waarom de gemiddelde datalekmelding nog steeds 277 dagen duurt om te ontdekken.

Samenvatting

Configureer TLS met alleen versie 1.2 en 1.3, gebruik ECDHE cipher suites voor forward secrecy, automatiseer certificaatbeheer met Let’s Encrypt, en schakel HSTS en OCSP stapling in. Test met testssl.sh of SSL Labs voor een A+ rating. Dit is geen complexe taak — het is een middagproject dat je server voor jaren beschermt.

Op de hoogte blijven?

Ontvang maandelijks cybersecurity-inzichten in je inbox.

← Webbeveiliging ← Home