HSTS instellen zonder jezelf buiten te sluiten
Een gefaseerd, omkeerbaar uitrolplan voor Strict-Transport-Security dat de privacy verbetert zonder dat één slecht certificaat een storing wordt.
Inhoudsopgave
- HSTS is eenvoudig totdat het dat niet meer is
- Wat de HSTS-header echt doet
- De buitensluitingsscenario’s die je wilt vermijden
- 1. Een vergeten subdomein is niet klaar voor HTTPS
- 2. Een certificaat verloopt
- 3. Staging- of interne tools staan onder het productiedomein
- 4. Preload wordt behandeld als een gewone checkbox
- Een veilig uitrolplan
- Step 1: Audit elke hostname die je beheert
- Step 2: Repareer HTTPS voordat je HSTS toevoegt
- Step 3: Begin met een zeer korte max-age
- Step 4: Verhoog geleidelijk
- Step 5: Voeg includeSubDomains pas toe nadat de audit echt is
- Step 6: Behandel preload als een apart project
- Configuratievoorbeelden
- Nginx
- Apache
- CDN of edge-platform
- HSTS veilig terugdraaien
- Testchecklist voordat je uitrolt
- Het privacyargument voor HSTS
HSTS is eenvoudig totdat het dat niet meer is
HTTP Strict Transport Security, meestal afgekort tot HSTS, vertelt browsers: “gebruik voor deze site altijd HTTPS.” Zodra een browser de header via een geldige HTTPS-verbinding ontvangt, onthoudt hij de regel gedurende de periode die je opgeeft.
Dat is nuttig. Het voorkomt protocol-downgradeaanvallen, vermindert onbedoelde onveilige requests en voorkomt het ongemakkelijke moment waarop een gebruiker example.com typt en kort plain HTTP raakt voordat er wordt omgeleid.
Het is ook hardnekkig. Als je het verkeerde HSTS-beleid publiceert, kunnen browsers dat blijven afdwingen lang nadat je de header van je server hebt verwijderd. Zo sluiten teams zichzelf buiten: niet precies uit hun eigen adminpaneel, maar uit de browsers van gebruikers, subdomeinen, staging-systemen, legacy-endpoints en vergeten services die nog niet klaar zijn voor verplichte HTTPS.
Het doel is niet om HSTS te vermijden. Het doel is om het te implementeren als een migratie, niet als een schakelaar.
Wat de HSTS-header echt doet
Een typische HSTS-header ziet er zo uit:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Hij heeft drie belangrijke onderdelen:
max-age: hoe lang, in seconden, de browser HTTPS voor deze host moet afdwingen.includeSubDomains: of de regel ook geldt voor elk subdomein.preload: een signaal dat je het domein wilt laten opnemen in preload-lijsten van browsers.
De browser vertrouwt deze header alleen wanneer hij die via geldige HTTPS ontvangt. Als het certificaat ongeldig, verlopen of niet passend is, hoort de browser geen nieuw HSTS-beleid uit die response te accepteren.
Zodra het beleid is opgeslagen, worden toekomstige pogingen om http://example.com te bezoeken door de browser opgewaardeerd naar https://example.com voordat de request wordt verzonden. Dat is de privacywinst: de onveilige request verlaat het apparaat nooit.
De buitensluitingsscenario’s die je wilt vermijden
De meeste HSTS-fouten worden niet veroorzaakt door de hoofdwebsite. Ze gebeuren aan de randen.
1. Een vergeten subdomein is niet klaar voor HTTPS
includeSubDomains klinkt netjes, maar is absoluut. Als je het instelt op example.com, geldt het voor:
www.example.comapi.example.comold-crm.example.comprinter-setup.example.comstaging.example.com- alles daaronder binnen dat domein
Als een van die hosts geen geldige HTTPS kan serveren, kunnen gebruikers met het HSTS-beleid in de cache ze niet meer via HTTP bereiken.
2. Een certificaat verloopt
Zonder HSTS klikken gebruikers soms door certificaatwaarschuwingen heen. Dat is geen goede beveiligingspraktijk, maar het gebeurt.
Met HSTS laten moderne browsers geen eenvoudige bypass van certificaatfouten toe voor die host. Dat is precies de bedoeling. Het betekent ook dat certificaatvernieuwing saai, gemonitord en getest moet zijn.
3. Staging- of interne tools staan onder het productiedomein
Interne tools onder *.example.com plaatsen kan pijnlijk worden zodra het hoofddomein includeSubDomains gebruikt. Als die tools self-signed certificaten, private certificate authorities, oude TLS-configuraties of helemaal geen HTTPS gebruiken, maakt HSTS die shortcut zichtbaar.
Dit is een reden waarom veel teams interne en experimentele systemen onder een apart domein houden met een eigen beveiligingsbeleid.
4. Preload wordt behandeld als een gewone checkbox
HSTS preload is niet zomaar nog een directive. Het betekent dat je domein in browsers kan worden meegeleverd als HTTPS-only voordat een gebruiker je site ooit heeft bezocht.
Dat sluit het gat van het “eerste bezoek”, maar is veel moeilijker terug te draaien. Verwijdering uit preload-lijsten kan weken of maanden duren voordat die gebruikers bereikt, afhankelijk van browser-releasecycli. Preload is geschikt voor stabiele, volwassen domeinen. Het is niet geschikt voor een site die zijn subdomeininventaris nog aan het ontdekken is.
Een veilig uitrolplan
Step 1: Audit elke hostname die je beheert
Voordat je includeSubDomains instelt, maak je een lijst van elke hostname onder het domein. DNS-records zijn een begin, maar niet het hele verhaal. Controleer CDN-configuraties, hostingdashboards, e-mailgerelateerde hostnames, oude marketingtools, storage buckets en interne documentatie.
Beantwoord voor elke hostname:
- Serveert hij HTTP, HTTPS of beide?
- Is het HTTPS-certificaat geldig en wordt het automatisch vernieuwd?
- Leidt HTTP netjes om naar HTTPS?
- Is hij bedoeld om publiek te zijn?
- Is hij nog nodig?
Als je team al gewoonten heeft voor het debuggen van productieheaders, past dit vanzelf naast redirect- en headercontroles. We hebben die workflow behandeld in een kleine toolkit voor het debuggen van redirects en HTTP-headers in productie.
Step 2: Repareer HTTPS voordat je HSTS toevoegt
HSTS maakt een kapotte HTTPS-configuratie niet veilig. Het maakt HTTPS alleen verplicht.
Controleer voordat je het inschakelt:
- TLS-certificaten dekken de juiste hostnames.
- Certificaten worden automatisch vernieuwd.
- HTTP leidt om naar HTTPS met waar mogelijk één nette sprong.
- Canonical-host-redirects zijn consistent, bijvoorbeeld non-
wwwnaarwww, of andersom. - Applicatie-assets zijn niet afhankelijk van onveilige
http://-URL’s.
Mixed content komt minder vaak voor dan vroeger, maar verschijnt nog steeds in oude CMS-thema’s, analytics-snippets, embedded media en hard-gecodeerde afbeeldingspaden.
Step 3: Begin met een zeer korte max-age
Begin niet met één jaar. Begin met vijf minuten:
Strict-Transport-Security: max-age=300
Deploy dat alleen op de hostname die je test, meestal de canonical productiewebsite. Laat includeSubDomains voorlopig weg.
Test daarna in echte browsers en met command-line requests:
curl -I https://example.com
Je zou precies één Strict-Transport-Security-header moeten zien. Dubbele HSTS-headers van een appserver en een CDN zijn een veelvoorkomende bron van verwarring. Browsers passen doorgaans het effectieve beleid toe, maar mensen die een incident debuggen hebben geen behoefte aan ambiguïteit.
Step 4: Verhoog geleidelijk
Als er niets breekt, verhoog je de duur in stappen:
Strict-Transport-Security: max-age=86400
Daarna:
Strict-Transport-Security: max-age=604800
En daarna misschien:
Strict-Transport-Security: max-age=2592000
Een praktisch schema is:
- 5 minuten
- 1 dag
- 1 week
- 1 maand
- 6 maanden of 1 jaar
Er is geen prijs voor haast. Het hele punt van een gefaseerde uitrol is dat je monitoring, support-inbox en edgecases tijd krijgen om je te vertellen wat je checklist heeft gemist.
Step 5: Voeg includeSubDomains pas toe nadat de audit echt is
Zodra elk publiek subdomein klaar is voor HTTPS, kun je dit overwegen:
Strict-Transport-Security: max-age=31536000; includeSubDomains
Dit is het moment om conservatief te zijn. Als één legacy-service nog HTTP nodig heeft, voeg dan geen includeSubDomains toe aan het hoofddomein. Migreer die service, verplaats hem naar een ander domein, of accepteer dat je HSTS-beleid voorlopig smaller moet blijven.
Security headers moeten de werkelijkheid weerspiegelen. Ze moeten niet worden gebruikt als motiverende posters voor infrastructuur die je later hoopt te hebben.
Step 6: Behandel preload als een apart project
Overweeg preload alleen wanneer al het volgende waar is:
- Het domein en alle subdomeinen ondersteunen geldige HTTPS.
- HTTP leidt om naar HTTPS.
- De HSTS-header gebruikt een
max-agevan minstens 31536000 seconden. - De header bevat
includeSubDomains. - De header bevat
preload. - Je weet zeker dat je nergens onder het domein plain HTTP nodig zult hebben.
Een preload-klare header ziet er zo uit:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Indienen bij de preload-lijst is een langetermijnverbintenis. Als de site een campagne-microsite, een tijdelijk productdomein of een domein met onduidelijke eigendomsgrenzen is, sla het dan over.
Configuratievoorbeelden
Nginx
Gebruik always zodat de header ook op foutresponses wordt verzonden:
add_header Strict-Transport-Security "max-age=300" always;
Nadat de uitrol stabiel is:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Apache
Met mod_headers ingeschakeld:
Header always set Strict-Transport-Security "max-age=300"
Later:
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
CDN of edge-platform
Als je CDN responseheaders instelt, beheer HSTS dan bij voorkeur op één plek. Stel niet één beleid in bij de origin en een ander aan de edge, tenzij je daar een heel duidelijke reden voor hebt.
Controleer ook of het CDN headers toepast op redirects, gecachete fouten en aangepaste foutpagina’s. Een productiesite is niet alleen zijn 200 OK-response.
HSTS veilig terugdraaien
Als je HSTS moet uitschakelen, stuur dan:
Strict-Transport-Security: max-age=0
Maar er zit een addertje onder het gras: de browser moet de site succesvol via geldige HTTPS kunnen bereiken om die header te ontvangen. Als HTTPS zelf kapot is, kunnen gebruikers met een HSTS-beleid in de cache de instructie die het zou wissen niet ophalen.
De gebruikelijke herstelvolgorde is dus:
- Herstel geldige HTTPS.
- Serveer
Strict-Transport-Security: max-age=0. - Laat dit lang genoeg staan zodat terugkerende gebruikers het ontvangen.
- Verwijder of vervang de header nadat het incident is opgelost.
Als het domein is gepreload, is max-age=0 serveren niet genoeg voor nieuwe browserprofielen. Je moet ook verwijdering uit de preload-lijst aanvragen en wachten tot die wijziging via browserupdates wordt verspreid.
Testchecklist voordat je uitrolt
Gebruik deze checklist voordat je max-age verhoogt of includeSubDomains toevoegt:
- De canonical HTTPS-URL retourneert een geldig certificaat.
- HTTP leidt om naar HTTPS.
- Er is maar één HSTS-header.
- De header verschijnt op redirects en foutresponses waar dat passend is.
- Alle publieke subdomeinen hebben geldige HTTPS.
- Certificaatvernieuwing wordt gemonitord.
- Geen kritiek intern systeem is afhankelijk van HTTP onder hetzelfde hoofddomein.
- Preload is expliciet besproken, niet uit gewoonte toegevoegd.
Lighthouse kan in sommige contexten ook ontbrekende of zwakke security headers markeren, maar het zou niet je enige verificatiemethode moeten zijn. Als je het gebruikt als onderdeel van een bredere review, lees de bevindingen dan als signalen in plaats van uitspraken; dezelfde houding geldt wanneer je een Lighthouse-rapport leest zonder in paniek te raken.
<!-- tool-cta:start -->
💡 Probeer dit: Controleer vóór en na elke HSTS-wijziging de Strict-Transport-Security-response met Get Headers om te bevestigen dat max-age, includeSubDomains en preload zijn zoals je verwacht.
<!-- tool-cta:end -->
Het privacyargument voor HSTS
HSTS wordt vaak gepresenteerd als een security header, en dat is het ook. Het heeft ook een privacyvoordeel: het verkleint de kans dat de eerste request van een gebruiker via plain HTTP lekt op een niet-vertrouwd netwerk.
Dat doet ertoe op luchthaven-wifi, hotelnetwerken, zakelijke gastnetwerken en overal waar verkeer van een gebruiker kan worden geobserveerd of aangepast. Een plain HTTP-request kan de hostname, het pad, cookies zonder de Secure-flag en andere requestdetails blootleggen. HTTPS is geen magie, maar het consequent afdwingen ervan verwijdert een hele klasse van vermijdbare lekkage.
De beste HSTS-implementaties zijn oninteressant. Ze worden langzaam uitgerold, worden ondersteund door betrouwbare certificaten en zijn saai genoeg dat niemand ze opmerkt. Dat is precies wat je wilt van een header waarvan de faalmodus dramatisch kan zijn.