# XSS in PHP verhindern: Cross-Site-Scripting sicher abwehren

> XSS in PHP verhindern: Ausgaben je Kontext richtig kodieren, Templates mit Auto-Escaping nutzen und Content Security Policy setzen. Mit Beispielen.

Quelle: https://phpsos.de/ratgeber/xss-php/  
Stand: 25.10.2026  
Anbieter: PHP SOS, PHP-Notfallhilfe und Server-Sicherheit. Soforthilfe per WhatsApp: https://wa.me/491736494914?text=Hallo%20PHP%20SOS%2C%20wir%20brauchen%20Hilfe%20mit%20unserem%20PHP-Server.%20Firma%3A%20%E2%80%A6%20Website%3A%20%E2%80%A6

**Kurz gesagt:** XSS in PHP verhindern Sie, indem Sie jede Ausgabe passend zu ihrem Kontext kodieren: `htmlspecialchars()` für HTML, `json_encode()` mit Schutzoptionen für JavaScript, geprüfte URLs für Links. Templates mit automatischem Escaping nehmen Ihnen viel davon ab. Eine Content Security Policy und HttpOnly-Cookies begrenzen den Schaden, falls doch eine Lücke bleibt.

## Warum Cross-Site-Scripting jetzt mitgeprüft werden sollte

Die Aufmerksamkeit liegt im Oktober 2026 auf PHP2Shell, zwei kritischen Lücken im PHP-Interpreter, für die seit dem 21.10.2026 Patches vorliegen. Viele Unternehmen lassen ihre Webanwendungen in diesem Zuge überprüfen. Das ist ein guter Anlass, auch Schwachstellen im eigenen Code anzusehen, die kein PHP-Update behebt.

Cross-Site-Scripting, kurz XSS, ist eine davon. Die OWASP Top 10 führen XSS innerhalb der Kategorie Injection. Anders als bei einer Lücke im Server richtet sich der Angriff hier gegen die Besucher und Nutzer Ihrer Website, etwa gegen Kunden im Shop oder Mitarbeitende im Backend.

Die Folgen können erheblich sein: übernommene Sitzungen von Administratoren, manipulierte Formulare, die Daten an Dritte senden, oder gefälschte Inhalte auf Ihrer Website. Wird ein Administratorkonto übernommen, kann das bis zur vollständigen Kontrolle über die Anwendung führen.

## Wie entsteht XSS in PHP-Anwendungen?

XSS entsteht, wenn eine Anwendung Daten aus einer nicht vertrauenswürdigen Quelle ungeprüft in eine Seite ausgibt. Der Browser kann dann nicht mehr zwischen Ihren Inhalten und eingeschleustem Code unterscheiden und führt beides aus.

Man unterscheidet drei Formen. Bei reflektiertem XSS stammt der Inhalt direkt aus der aktuellen Anfrage, etwa aus einem Suchbegriff in der URL. Bei gespeichertem XSS liegt er in der Datenbank, zum Beispiel in einem Kommentar oder Profilfeld, und trifft jeden, der die Seite aufruft. DOM-basiertes XSS entsteht im JavaScript des Browsers, wenn Skripte Werte aus der URL in die Seite schreiben.

Nicht vertrauenswürdig sind alle Daten, die nicht fest im Code stehen: Formularfelder, URL-Parameter, Cookies, Dateinamen, Antworten externer Schnittstellen und Inhalte aus der eigenen Datenbank, die irgendwann von außen kamen.

## Ausgabe je Kontext richtig kodieren

Die wichtigste Regel lautet: Kodieren bei der Ausgabe, und zwar passend zum Ort, an dem der Wert landet. Für HTML-Inhalt und Attribute in Anführungszeichen ist `htmlspecialchars()` das Mittel der Wahl. Eine kleine Hilfsfunktion verhindert, dass Optionen vergessen werden:

```php
<?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 Kontexte brauchen andere Mittel:

| Kontext | Sichere Methode |
|---|---|
| HTML-Inhalt, Attribut in Anführungszeichen | `htmlspecialchars()` mit `ENT_QUOTES` und UTF-8 |
| Daten für JavaScript | `json_encode()` mit `JSON_HEX_TAG`, `JSON_HEX_AMP`, `JSON_HEX_APOS`, `JSON_HEX_QUOT` |
| Parameter in einer URL | `rawurlencode()` für den einzelnen Wert |
| Vollständige URL aus Benutzereingabe | Schema prüfen, nur `https` und `http` zulassen, dann `htmlspecialchars()` |
| Attribute ohne Anführungszeichen, Event-Handler, CSS | vermeiden, Werte dort nicht einsetzen |

Für Daten, die ein Skript braucht, ist ein Datenattribut oder ein JSON-Block meist der sauberste Weg:

```php
<?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>
```

Im JavaScript lesen Sie den Wert dann über das Datenattribut aus und setzen Inhalte mit `textContent` statt mit `innerHTML`.

## Templates, HTML-Bereinigung und Frameworks

Template-Engines wie Twig und Blade kodieren Ausgaben standardmäßig automatisch. Das schließt einen Großteil typischer Fehler aus. Gefährlich wird es dort, wo dieses Verhalten bewusst abgeschaltet wird, etwa mit dem Filter `raw` in Twig oder der Syntax `{!! !!}` in Blade. Solche Stellen sollten im Code selten sein und bei jedem Review gezielt geprüft werden.

Soll Ihre Anwendung bewusst HTML von Nutzern zulassen, etwa Formatierungen in Kommentaren oder in einem Redaktionssystem, reicht Kodieren nicht, weil das HTML ja dargestellt werden soll. Dann brauchen Sie eine geprüfte Bereinigungsbibliothek wie HTML Purifier, die nur eine feste Liste erlaubter Elemente und Attribute durchlässt. Eigene Filter mit regulären Ausdrücken sind erfahrungsgemäß lückenhaft.

> **Wichtig:**
> Prüfen Sie besonders Backend-Bereiche. XSS in Ansichten, die nur Administratoren sehen, wird oft unterschätzt, trifft aber genau die Konten mit den meisten Rechten.

## XSS in PHP-Bestandscode finden

In gewachsenen Anwendungen ist die erste Frage meist nicht, wie man richtig kodiert, sondern wo es bisher vergessen wurde. Ein pragmatischer Einstieg ist die Suche nach Stellen, an denen Variablen direkt ausgegeben werden:

```bash
# Direkte Ausgaben von Anfragedaten
grep -rnE "(echo|print|<\?=)\s*\\\$_(GET|POST|REQUEST|COOKIE|SERVER)" src/ templates/ --include="*.php"
# Abgeschaltetes Auto-Escaping in Twig und Blade
grep -rnE "\|raw|\{!!" templates/ resources/views/
```

Treffer sind Prüfaufträge, keine fertigen Befunde. Gespeichertes XSS findet eine solche Suche nur bedingt, weil die Werte dort aus der Datenbank kommen. Prüfen Sie deshalb zusätzlich alle Ansichten, die Inhalte von Nutzern anzeigen: Kommentare, Profile, Bestellnotizen, Support-Tickets. Bei CMS und Shops gehören Plugins und Themes von Drittanbietern ausdrücklich dazu, denn dort sitzt ein großer Teil der Ausgabelogik.

## Zweite Verteidigungslinie: CSP und sichere Cookies

Auch bei sorgfältigem Code kann eine Stelle übersehen werden. Zwei Maßnahmen begrenzen dann den Schaden. Eine Content Security Policy erlaubt dem Browser nur Skripte aus festgelegten Quellen und verbietet eingebettete Skripte, sofern sie nicht ausdrücklich freigegeben sind:

```apache
Header always set Content-Security-Policy "default-src 'self'; script-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
```

Führen Sie eine solche Richtlinie schrittweise ein, zunächst im reinen Berichtsmodus über `Content-Security-Policy-Report-Only`, denn bestehende Einbindungen von Analyse- oder Zahlungsdiensten müssen meist ergänzt werden. Weitere Header beschreibt der Ratgeber [Sicherheits-Header für Websites](https://phpsos.de/ratgeber/sicherheits-header/).

Zusätzlich sollten Sitzungscookies das Attribut `HttpOnly` tragen, damit Skripte sie nicht auslesen können. Wie Sie Sessions insgesamt absichern, steht im Ratgeber [Sessions in PHP absichern](https://phpsos.de/ratgeber/session-sicherheit-php/). Eine verwandte Schwachstelle auf der Datenbankseite behandelt [SQL-Injection in PHP verhindern](https://phpsos.de/ratgeber/sql-injection-php/).

## Wie hilft PHP SOS konkret?

- Wir prüfen Templates und Ausgaben Ihrer Anwendung auf fehlende oder falsche Kodierung.
- Wir bewerten Funde nach Erreichbarkeit und betroffenen Konten und zeigen konkrete Korrekturen.
- Wir führen eine Content Security Policy schrittweise ein, ohne Shop- oder Zahlungsfunktionen zu stören.
- Wir testen nach der Korrektur erneut und dokumentieren das Ergebnis.

Eine unabhängige Prüfung beschreibt die Leistung [Sicherheitsaudit und Penetrationstest](https://phpsos.de/leistungen/sicherheitsaudit-penetrationstest/).


## Häufige Fragen

**Reicht htmlspecialchars gegen XSS?**  
Für normalen Text im HTML-Inhalt und in Attributen in Anführungszeichen ja, wenn Sie ENT_QUOTES und den Zeichensatz UTF-8 angeben. In JavaScript, in URLs oder in CSS gelten andere Regeln, dort braucht es eine eigene, passende Kodierung.

**Sollte ich Eingaben beim Speichern oder bei der Ausgabe filtern?**  
Kodieren Sie bei der Ausgabe, passend zum Ziel. Eingaben sollten Sie beim Empfang auf Plausibilität prüfen, etwa Länge und Format. Wer nur beim Speichern filtert, übersieht Daten aus anderen Quellen und erschwert spätere Nutzung in anderen Kontexten.

**Was bringt eine Content Security Policy?**  
Eine Content Security Policy legt fest, aus welchen Quellen der Browser Skripte laden und ausführen darf. Sie kann die Auswirkungen einer übersehenen XSS-Lücke deutlich begrenzen, ersetzt aber keine korrekte Kodierung.

**Wie erlaube ich Formatierungen wie fett oder Links in Kommentaren?**  
Nutzen Sie eine geprüfte HTML-Bereinigungsbibliothek mit einer Whitelist erlaubter Elemente und Attribute. Eigene reguläre Ausdrücke zum Entfernen von Skripten sind fehleranfällig und sollten nicht verwendet werden.

## Quellen

- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/)
- [php.net: Sicherheitsmeldungen](https://www.php.net/)
