X-Frame-Options is een HTTP-response-header die de browser vertelt of uw pagina mag worden weergegeven in een <iframe>, <frame> of <object>-element. Het primaire doel is het voorkomen van clickjacking-aanvallen: een klasse van aanvallen waarbij een kwaadaardige website uw site onzichtbaar insluit en bezoekers verleidt onbedoelde acties uit te voeren.
Hoewel X-Frame-Options een van de oudere beveiligingsheaders is (geïntroduceerd rond 2009), wordt hij nog altijd breed ingezet en biedt hij belangrijke bescherming. Laten we kijken waarom hij ertoe doet, hoe hij werkt en wat WordPress-site-eigenaren moeten weten.
Hoe clickjacking-aanvallen daadwerkelijk werken
Clickjacking is conceptueel verrassend eenvoudig. Stel dat u een WordPress-site beheert met een ledengedeelte, en de profielpagina heeft een knop "Mijn account verwijderen". Een aanvaller maakt een pagina die op een leuke quiz of game lijkt. Hij sluit uw profielpagina in een onzichtbaar iframe in, exact bovenop zijn knop "Klik hier voor uw resultaat".
Wanneer de bezoeker op wat hij denkt dat een onschuldige knop is op de pagina van de aanvaller klikt, klikt hij in werkelijkheid op de knop "Mijn account verwijderen" op uw verborgen pagina. Is de bezoeker ingelogd op uw site (en de meeste browsers bewaren sessiecookies), dan wordt de actie uitgevoerd. De bezoeker heeft geen idee wat er zojuist is gebeurd.
Deze techniek is bij echte aanvallen gebruikt om:
- Privacy-instellingen op social-media-accounts te wijzigen
- OAuth-toepassingen te autoriseren zonder medeweten van de gebruiker
- Op advertenties te klikken om frauduleuze inkomsten te genereren
- Toegang tot webcam of microfoon mogelijk te maken in oudere browsers
- Geld over te boeken in onlinebankieromgevingen
De aanval werkt omdat de browser geen ingebouwde manier heeft om te weten dat het iframe kwaadwillig wordt gebruikt. X-Frame-Options biedt uw server een manier om volledig af te zien van inframing.
Waarden van de X-Frame-Options-header uitgelegd
De header ondersteunt twee praktische waarden (plus een derde, verouderde):
DENY: de pagina mag in geen enkel frame worden weergegeven, punt. Geen enkele website, ook uw eigen niet, kan deze pagina in een iframe insluiten. Dit is de strengste instelling en past goed bij pagina's die nooit ingelijst mogen worden, zoals inlogpagina's of beheerpanelen.SAMEORIGIN: de pagina mag alleen in een frame worden weergegeven als de inlijstende site dezelfde origin heeft (zelfde protocol, domein en poort). Dit is de meest gebruikte waarde, omdat hij toestaat dat uw eigen site iframes gebruikt en tegelijkertijd externe sites blokkeert.ALLOW-FROM uri: dit moest u één toegestane origin laten opgeven die uw pagina mocht inlijsten. Het is echter nooit consistent ondersteund door browsers. Chrome en Safari hebben het nooit geïmplementeerd. Firefox ondersteunde het een tijdje, maar liet de ondersteuning vallen. Deze waarde is in de praktijk dood en moet niet gebruikt worden.
Wanneer DENY versus SAMEORIGIN gebruiken op WordPress
Voor de meeste WordPress-sites is SAMEORIGIN de juiste keuze. Hier is waarom:
WordPress zelf gebruikt op verschillende plekken iframes. De thema-Customizer laadt een voorbeeld van uw site in een iframe. De uploader van de mediabibliotheek gebruikt iframes. Het dialoog "Media invoegen" van de klassieke editor ook. Stelt u DENY in, dan breken al deze functies, omdat zelfs uw eigen domein de pagina niet meer mag inlijsten.
DENY is zinvol voor specifieke pagina's waar inlijsten nooit zou moeten gebeuren, zoals een losstaande inlogpagina of een betaalpagina. Maar als site-brede standaard voor WordPress biedt SAMEORIGIN de bescherming die u nodig heeft zonder de admin-functionaliteit te breken.
Draait u een headless WordPress-opzet waarbij de admin-interface op één domein staat en de front-end op een ander, dan moet u hier extra voorzichtig zijn, want SAMEORIGIN staat alleen inlijsten toe vanaf exact dezelfde origin.
Waarom ALLOW-FROM is afgeschaft
Het afschaffen van ALLOW-FROM is het begrijpen waard, omdat het een breder punt over webbeveiligingsheaders illustreert. De directive maakte deel uit van de oorspronkelijke X-Frame-Options-specificatie en het idee was eenvoudig: laat een site zeggen "alleen dit specifieke domein mag mij inlijsten". In de praktijk had het diverse problemen:
- Het accepteerde slechts één URI, dus u kon geen meerdere domeinen op de witte lijst zetten
- Chrome heeft het nooit geïmplementeerd (de meest gebruikte browser ter wereld negeerde het simpelweg)
- Safari heeft het ook nooit ondersteund
- Het gedrag was inconsistent in de browsers die het wel ondersteunden
De CSP-directive frame-ancestors is ontworpen als de juiste vervanger, met ondersteuning voor meerdere origins en consistent cross-browser-gedrag. Moet u specifieke externe domeinen toestaan om uw pagina's in te sluiten, dan is frame-ancestors de manier.
X-Frame-Options versus CSP frame-ancestors
De directive frame-ancestors van de Content-Security-Policy-header dient hetzelfde doel als X-Frame-Options, maar met meer flexibiliteit:
Content-Security-Policy: frame-ancestors 'self'Dit is gelijkwaardig aan X-Frame-Options: SAMEORIGIN. Maar frame-ancestors ondersteunt ook meerdere origins:
Content-Security-Policy: frame-ancestors 'self' https://trusted-partner.com https://another-domain.comDat zou met X-Frame-Options alleen onmogelijk zijn.
Wanneer beide headers aanwezig zijn, varieert het browsergedrag. Moderne browsers geven over het algemeen voorrang aan frame-ancestors boven X-Frame-Options. De aanbevolen praktijk is echter om beide headers te versturen voor maximale compatibiliteit. Oudere browsers die geen CSP begrijpen, vallen terug op X-Frame-Options, terwijl moderne browsers frame-ancestors gebruiken.
Hier is een voorbeeld van het samen gebruiken van beide:
X-Frame-Options: SAMEORIGIN
Content-Security-Policy: frame-ancestors 'self'X-Frame-Options instellen voor WordPress
WordPress verstuurt standaard X-Frame-Options: SAMEORIGIN voor het admin-gedeelte (wp-admin). Dit gebeurt in wp-includes/functions.php via de functie send_frame_options_header(). De front-end van uw site krijgt deze header echter niet standaard.
Om hem site-breed via Apache toe te voegen, plaatst u dit in uw .htaccess:
Header always set X-Frame-Options "SAMEORIGIN"Voor Nginx:
add_header X-Frame-Options "SAMEORIGIN" always;U kunt hem ook via PHP instellen in uw thema of een plugin:
add_action('send_headers', function() {
header('X-Frame-Options: SAMEORIGIN');
});Gebruikt u plugins die uw site moeten insluiten in een iframe op een ander domein (sommige A/B-testtools, livechat-widgets of voorbeeldservices), dan moet u uw aanpak mogelijk aanpassen. Gebruik in dat geval CSP frame-ancestors om die specifieke domeinen op de witte lijst te zetten in plaats van X-Frame-Options helemaal te verwijderen.
Wat InspectWP controleert
InspectWP verifieert of uw WordPress-site de header X-Frame-Options verstuurt op zijn front-end-pagina's. Ontbreekt de header, dan signaleert het rapport dit als een beveiligingsprobleem, omdat elke website uw pagina's in een iframe kan insluiten en zo clickjacking-aanvallen tegen uw bezoekers mogelijk kan maken. Het rapport vermeldt ook de waarde van de header indien aanwezig, zodat u kunt verifiëren dat deze op SAMEORIGIN of DENY staat, afhankelijk van uw opzet.