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 offis 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:
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-nginxCertificaat 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.comAutomatische 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-hookom 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:
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.comCertificaatbeheer
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:
- Zodra je domein in de preload-lijst staat, kan het maanden duren om het te verwijderen
- Als een subdomein geen HTTPS heeft en je gebruikt
includeSubDomains, is dat subdomein onbereikbaar - 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
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.comtestssl.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 |
| HTTP → HTTPS 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.
Verder lezen in de kennisbank
Deze artikelen in het portaal geven je meer achtergrond en praktische context:
- API's — de onzichtbare lijm van het internet
- SSL/TLS — waarom dat slotje in je browser ertoe doet
- Encryptie — de kunst van het onleesbaar maken
- Wachtwoord-hashing — hoe websites je wachtwoord opslaan
- Penetratietesten vs. vulnerability scans
Je hebt een account nodig om de kennisbank te openen. Inloggen of registreren.
Gerelateerde securitymaatregelen
Deze artikelen bieden aanvullende context en verdieping: