Actuele situatie: PHP2Shell. Twee kritieke PHP-kwetsbaarheden (CVSS 9.2) maken schadelijke code zonder inloggen mogelijk. Patches sinds 21-10-2026.Wat u nu moet doen →

XSS in PHP voorkomen: cross-site scripting veilig afweren

Hoe ontwikkelaars cross-site scripting in PHP-toepassingen voorkomen: contextgerichte codering, auto-escaping in templates, opschoning van HTML en een Content Security Policy.

Stand 5 min. leestijdGidsen

Kort gezegd: XSS in PHP voorkomt u door elke uitvoer passend bij haar context te coderen: htmlspecialchars() voor HTML, json_encode() met beschermingsopties voor JavaScript, gecontroleerde URL's voor links. Templates met automatisch escapen nemen u veel daarvan uit handen. Een Content Security Policy en HttpOnly-cookies beperken de schade als er toch een kwetsbaarheid overblijft.

Waarom cross-site scripting nu mee gecontroleerd moet worden

De aandacht ligt in oktober 2026 bij PHP2Shell, twee kritieke kwetsbaarheden in de PHP-interpreter waarvoor sinds 21-10-2026 patches beschikbaar zijn. Veel bedrijven laten in dat kader hun webtoepassingen controleren. Dat is een goede aanleiding om ook zwakke plekken in de eigen code te bekijken die geen PHP-update verhelpt.

Cross-site scripting, kortweg XSS, is er één van. De OWASP Top 10 plaatsen XSS binnen de categorie injection. Anders dan bij een kwetsbaarheid in de server richt de aanval zich hier tegen de bezoekers en gebruikers van uw website, bijvoorbeeld tegen klanten in de webshop of medewerkers in de backend.

De gevolgen kunnen aanzienlijk zijn: overgenomen sessies van beheerders, gemanipuleerde formulieren die gegevens naar derden sturen, of vervalste inhoud op uw website. Wordt een beheerdersaccount overgenomen, dan kan dat leiden tot volledige controle over de toepassing.

Hoe ontstaat XSS in PHP-toepassingen?

XSS ontstaat wanneer een toepassing gegevens uit een niet-vertrouwde bron ongecontroleerd op een pagina weergeeft. De browser kan dan niet meer onderscheiden tussen uw inhoud en binnengebrachte code en voert beide uit.

Men onderscheidt drie vormen. Bij gereflecteerde XSS komt de inhoud rechtstreeks uit het huidige verzoek, bijvoorbeeld uit een zoekterm in de URL. Bij opgeslagen XSS staat ze in de database, bijvoorbeeld in een reactie of profielveld, en treft iedereen die de pagina opent. DOM-gebaseerde XSS ontstaat in het JavaScript van de browser, wanneer scripts waarden uit de URL naar de pagina schrijven.

Niet te vertrouwen zijn alle gegevens die niet vast in de code staan: formuliervelden, URL-parameters, cookies, bestandsnamen, antwoorden van externe koppelingen en inhoud uit de eigen database die ooit van buiten kwam.

Uitvoer per context correct coderen

De belangrijkste regel luidt: codeer bij de uitvoer, en wel passend bij de plek waar de waarde terechtkomt. Voor HTML-inhoud en attributen tussen aanhalingstekens is htmlspecialchars() het middel bij uitstek. Een kleine hulpfunctie voorkomt dat opties worden vergeten:

<?php
function e(?string $wert): string
{
    return htmlspecialchars($wert ?? '', ENT_QUOTES | ENT_SUBSTITUTE | ENT_HTML5, 'UTF-8');
}
?>
<h2><?= e($produkt['name']) ?></h2>
<input type="text" name="suche" value="<?= e($_GET['q'] ?? '') ?>">

Andere contexten vragen andere middelen:

Context Veilige methode
HTML-inhoud, attribuut tussen aanhalingstekens htmlspecialchars() met ENT_QUOTES en UTF-8
Gegevens voor JavaScript json_encode() met JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_HEX_QUOT
Parameter in een URL rawurlencode() voor de afzonderlijke waarde
Volledige URL uit gebruikersinvoer Schema controleren, alleen https en http toestaan, dan htmlspecialchars()
Attributen zonder aanhalingstekens, event-handlers, CSS vermijden, daar geen waarden invoegen

Voor gegevens die een script nodig heeft, is een data-attribuut of een JSON-blok meestal de schoonste weg:

<?php
$daten = json_encode($konfiguration, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT);
?>
<div id="app" data-konfig="<?= e($daten) ?>"></div>

In JavaScript leest u de waarde vervolgens uit via het data-attribuut en zet u inhoud met textContent in plaats van met innerHTML.

Templates, HTML-opschoning en frameworks

Template-engines als Twig en Blade coderen uitvoer standaard automatisch. Dat sluit een groot deel van de typische fouten uit. Gevaarlijk wordt het waar dit gedrag bewust wordt uitgeschakeld, bijvoorbeeld met het filter raw in Twig of de syntaxis {!! !!} in Blade. Zulke plekken moeten in de code zeldzaam zijn en bij elke review gericht worden gecontroleerd.

Moet uw toepassing bewust HTML van gebruikers toestaan, bijvoorbeeld opmaak in reacties of in een redactiesysteem, dan volstaat coderen niet, omdat de HTML juist moet worden weergegeven. Dan hebt u een geteste opschoningsbibliotheek nodig zoals HTML Purifier, die alleen een vaste lijst van toegestane elementen en attributen doorlaat. Eigen filters met reguliere expressies zijn volgens de ervaring lekkend.

Belangrijk

Controleer vooral backendgebieden. XSS in weergaven die alleen beheerders zien, wordt vaak onderschat, maar treft precies de accounts met de meeste rechten.

XSS in bestaande PHP-code vinden

In gegroeide toepassingen is de eerste vraag meestal niet hoe men correct codeert, maar waar het tot nu toe is vergeten. Een pragmatische start is het zoeken naar plekken waar variabelen rechtstreeks worden uitgevoerd:

# Directe uitvoer van aanvraaggegevens
grep -rnE "(echo|print|<\?=)\s*\\\$_(GET|POST|REQUEST|COOKIE|SERVER)" src/ templates/ --include="*.php"
# Uitgeschakeld auto-escaping in Twig en Blade
grep -rnE "\|raw|\{!!" templates/ resources/views/

Treffers zijn controleopdrachten, geen kant-en-klare bevindingen. Opgeslagen XSS vindt zo'n zoekopdracht maar beperkt, omdat de waarden daar uit de database komen. Controleer daarom aanvullend alle weergaven die inhoud van gebruikers tonen: reacties, profielen, bestelnotities, supporttickets. Bij CMS'en en webshops horen plug-ins en thema's van derden er uitdrukkelijk bij, want daar zit een groot deel van de uitvoerlogica.

Tweede verdedigingslinie: CSP en veilige cookies

Ook bij zorgvuldige code kan een plek over het hoofd worden gezien. Twee maatregelen beperken dan de schade. Een Content Security Policy staat de browser alleen scripts uit vastgelegde bronnen toe en verbiedt ingebedde scripts, tenzij ze uitdrukkelijk zijn vrijgegeven:

Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"

Voer zo'n beleid stapsgewijs in, eerst in de zuivere rapportagemodus via Content-Security-Policy-Report-Only, want bestaande inbedding van analyse- of betaaldiensten moet meestal worden aangevuld. Verdere headers beschrijft de gids Beveiligingsheaders voor websites.

Daarnaast moeten sessiecookies het attribuut HttpOnly dragen, zodat scripts ze niet kunnen uitlezen. Hoe u sessies als geheel beveiligt, staat in de gids Sessies in PHP beveiligen. Een verwante zwakke plek aan de databasekant behandelt SQL-injectie in PHP voorkomen.

Hoe helpt PHP SOS concreet?

  • We controleren templates en uitvoer van uw toepassing op ontbrekende of verkeerde codering.
  • We beoordelen vondsten naar bereikbaarheid en getroffen accounts en tonen concrete correcties.
  • We voeren een Content Security Policy stapsgewijs in, zonder webshop- of betaalfuncties te verstoren.
  • We testen na de correctie opnieuw en documenteren het resultaat.

Een onafhankelijke controle beschrijft de dienst Beveiligingsaudit en penetratietest.

Veelgestelde vragen

Volstaat htmlspecialchars tegen XSS?

Voor gewone tekst in HTML-inhoud en in attributen tussen aanhalingstekens wel, als u ENT_QUOTES en de tekenset UTF-8 opgeeft. In JavaScript, in URL's of in CSS gelden andere regels; daar is een eigen, passende codering nodig.

Moet ik invoer bij het opslaan of bij de uitvoer filteren?

Codeer bij de uitvoer, passend bij de bestemming. Invoer moet u bij ontvangst op plausibiliteit controleren, bijvoorbeeld lengte en formaat. Wie alleen bij het opslaan filtert, mist gegevens uit andere bronnen en bemoeilijkt later gebruik in andere contexten.

Wat levert een Content Security Policy op?

Een Content Security Policy legt vast uit welke bronnen de browser scripts mag laden en uitvoeren. Ze kan de gevolgen van een over het hoofd geziene XSS-kwetsbaarheid aanzienlijk beperken, maar vervangt geen correcte codering.

Hoe sta ik opmaak zoals vet of links in reacties toe?

Gebruik een geteste bibliotheek voor HTML-opschoning met een whitelist van toegestane elementen en attributen. Eigen reguliere expressies om scripts te verwijderen zijn foutgevoelig en moet u niet gebruiken.

Bronnen

Direct contact

Elk uur telt. Schrijf ons nu.

Beschrijf kort het systeem, de PHP-versie en wat u opmerkt. Wij nemen contact op, brengen de situatie in kaart en werken op afstand tot het incident is afgerond en gedocumenteerd.

Directe hulp