XSS: Was ist das und wie verhindern Sie es?
XSS (Cross-Site-Scripting) bedeutet, dass ein Angreifer ein eigenes Skript in eine Seite Ihrer Website einschleusen kann. Weil das Skript von der Adresse Ihrer Website kommt, vertraut ihm der Browser des Besuchers wie dem eigenen Code Ihrer Website: Es kann Inhalte der Seite verändern, im Namen des Benutzers Anfragen senden, ein gefälschtes Anmeldeformular anzeigen oder ein ungeschütztes Sitzungs-Cookie auslesen.
Ursache der Schwachstelle ist, dass vom Benutzer stammende Daten ohne Escaping in die Seite geschrieben werden. Darin liegt auch der Kern der Lösung: Jeder Wert wird so escapt, wie es der Kontext verlangt, in den er geschrieben wird. Diese Anleitung erklärt das Konzept, die Arten und die richtige Umsetzung mit PHP.
Kurz gefasst
- XSS entsteht, wenn Benutzerdaten ohne Escaping in die Seite geschrieben werden.
- Der Schutz erfolgt bei der Ausgabe: Jeder Wert wird passend zu dem Kontext escapt, in den er geschrieben wird.
- In JavaScript werden Daten mit
textContentgeschrieben; Rich Text wird anhand einer Positivliste bereinigt. - CSP und HttpOnly-Cookie sind die zweite Schicht, die den Schaden begrenzt, wenn ein Fehler passiert.
Auf dieser Seite
Wie entsteht XSS?
-
Drei Arten, eine Ursache
- Persistentes (stored) XSS: Der schädliche Inhalt wird in der Datenbank gespeichert (Kommentar, Profilname, Supportanfrage) und jedem angezeigt, der die betreffende Seite öffnet. Diese Art hat die größte Reichweite; Datensätze, die im Administrationsbereich angezeigt werden, treffen auch den Administrator.
- Reflektiertes (reflected) XSS: Der schädliche Inhalt stammt aus einem Parameter der Anfrage und wird unverändert in die Antwort zurückgeschrieben (Suchbegriff, Fehlermeldung). Der Benutzer muss dazu auf einen eigens präparierten Link klicken.
- DOM-basiertes XSS: Das Problem liegt nicht auf dem Server, sondern im JavaScript-Code im Browser; das Skript schreibt Daten, die es aus der Adresse oder aus einer anderen Quelle bezieht, als HTML in die Seite.
In allen drei Fällen ist die Ursache dieselbe: Nicht vertrauenswürdige Daten wurden ohne Escaping an eine Stelle der Seite gesetzt, an der sie als Code interpretiert werden können.
: Vergrößern -
Der Schutz erfolgt bei der Ausgabe
Statt Daten beim Speichern zu „bereinigen“, escapen Sie sie beim Schreiben in die Seite. Derselbe Wert kann heute in HTML, morgen in einer E-Mail oder in einer JSON-Antwort verwendet werden; für jeden dieser Fälle gilt eine andere Escaping-Regel. Die Rohdaten in der Datenbank zu speichern und bei der Ausgabe kontextgerecht zu escapen, ist sicher und zugleich konsistent.
: Vergrößern -
Der Kontext bestimmt das Escaping
HTML-Body, HTML-Attribut, JavaScript und URL-Parameter sind getrennte Kontexte, und jeder hat seine eigene Regel. Ein Escaping, das für HTML richtig ist, reicht innerhalb von JavaScript nicht aus. Im folgenden Abschnitt finden Sie für jeden Kontext das richtige Verfahren.
: Vergrößern -
Zweite Schichten: CSP und Cookie-Flags
Wird beim Escaping eine Stelle übersehen, braucht es Schichten, die den Schaden begrenzen: Die Content-Security-Policy teilt dem Browser mit, aus welchen Quellen er Skripte ausführen darf; das Flag HttpOnly verhindert, dass das Sitzungs-Cookie von JavaScript ausgelesen wird.
: Vergrößern
Anzeichen: Woran erkennt man das Problem?
- Im Code-Review: Ausgaben ohne Escaping wie
echo $_GET[...]oder<?= $variable ?>; auf der JavaScript-Seite Benutzerdaten, die mitinnerHTML,document.writeoder den jQuery-Methoden.html()und.append()in die Seite geschrieben werden. - Auf der Website: Wenn Sie in ein Feld einen harmlosen Text mit einem HTML-Tag eingeben (z. B. dem Tag für Fettschrift) und der Text nicht unverändert, sondern formatiert erscheint, wird dieses Feld nicht escapt. Probieren Sie das ausschließlich auf Ihrer eigenen Website aus.
- Beschwerden von Besuchern: Unerwartete Weiterleitungen, Pop-up-Fenster, Inhalte auf der Seite, die nicht von Ihnen stammen.
- CSP-Berichte: Ist das Reporting aktiviert, werden Versuche gemeldet, Skripte aus nicht erlaubten Quellen zu laden.
Kontextgerechtes Ausgabe-Escaping
HTML-Body und Attribute
Verwenden Sie in PHP die Funktion htmlspecialchars mit ENT_QUOTES und dem Zeichensatz. Eine kurze Hilfsfunktion sorgt dafür, dass überall dieselbe Einstellung verwendet wird. Setzen Sie Attributwerte immer in Anführungszeichen; bei einem Attribut ohne Anführungszeichen reicht das Escaping nicht aus.
<?php
// Eine einzige Hilfsfunktion: überall dieselbe Einstellung
function e($deger): string
{
return htmlspecialchars((string) $deger, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}
?>
<p>Hallo, <?= e($kullaniciAdi) ?></p>
<input type="text" name="q" value="<?= e($_GET['q'] ?? '') ?>">Daten an JavaScript übergeben
Wenn Sie Daten von PHP an JavaScript übergeben, schreiben Sie den Wert nicht von Hand in Anführungszeichen; übergeben Sie ihn mit json_encode und mit Flags, die HTML-relevante Zeichen umwandeln. Noch sicherer ist es, die Daten in ein data--Attribut zu schreiben und von JavaScript aus zu lesen.
<?php
$veri = ['ad' => $kullaniciAdi, 'sepet' => $sepetAdedi];
$bayraklar = JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_UNESCAPED_UNICODE;
?>
<script>
var sayfaVerisi = <?= json_encode($veri, $bayraklar) ?>;
</script>
<!-- Sicherere Variante: Daten über ein data-Attribut transportieren -->
<div id="kullanici" data-ad="<?= htmlspecialchars($kullaniciAdi, ENT_QUOTES, 'UTF-8') ?>"></div>URLs und Links
Daten, die in den Parameterwert einer URL geschrieben werden, werden mit rawurlencode kodiert. Soll eine vom Benutzer angegebene Adresse in href oder src geschrieben werden, prüfen Sie zusätzlich zum Escaping ihr Schema: Lassen Sie nur Adressen zu, die mit http oder https beginnen.
<?php
// Parameterwert: rawurlencode
$adres = 'arama?q=' . rawurlencode($terim);
// Vom Benutzer angegebene Adresse: nur die Schemata http/https zulassen
function guvenliAdres(string $adres): string
{
$sema = strtolower((string) parse_url($adres, PHP_URL_SCHEME));
return in_array($sema, ['http', 'https'], true) ? $adres : '#';
}
?>
<a href="<?= htmlspecialchars($adres, ENT_QUOTES, 'UTF-8') ?>">Ergebnisse</a>
<a href="<?= htmlspecialchars(guvenliAdres($profilSitesi), ENT_QUOTES, 'UTF-8') ?>" rel="noopener nofollow">Website</a>Stellen, die Sie meiden sollten
Schreiben Sie nicht vertrauenswürdige Daten nicht direkt in einen <script>-Block, nicht in Event-Attribute (wie onclick), nicht in <style> und nicht als Namen eines Tags oder Attributs. In diesen Kontexten ist ein sicheres Escaping schwierig; transportieren Sie die Daten über ein data--Attribut.
Sicherer Umgang auf der Browserseite (DOM)
Verwenden Sie in JavaScript beim Schreiben von Daten in die Seite keine Methoden, die den Wert als HTML interpretieren, sondern solche, die ihn als Text schreiben. textContent fügt die Daten immer als reinen Text ein.
// FALSCH: Die Daten werden als HTML interpretiert
// kutu.innerHTML = 'Hallo ' + ad;
// RICHTIG: Die Daten werden als reiner Text eingefügt
kutu.textContent = 'Hallo ' + ad;
// Beim Erzeugen eines neuen Elements
var satir = document.createElement('li');
satir.textContent = yorum.metin;
liste.appendChild(satir);React, Vue und ähnliche Frameworks escapen Werte in Templates standardmäßig. Das Risiko kehrt bei Funktionen zurück, die diesen Schutz bewusst abschalten (dangerouslySetInnerHTML, v-html). Übergeben Sie diesen Funktionen ausschließlich bereinigtes HTML.
Rich Text: Bereinigung anhand einer Positivliste
Muss der Benutzer in Feldern wie einem Blog-Editor oder einer Produktbeschreibung HTML eingeben können, lässt sich Escaping nicht anwenden, denn die Formatierung soll ja sichtbar sein. In diesem Fall wird das HTML anhand einer Positivliste bereinigt: Nur bestimmte Tags (etwa Absatz, Fettschrift, Liste, Link) und bestimmte Attribute sind erlaubt, alles andere wird verworfen.
- Schreiben Sie für die Bereinigung keine eigenen regulären Ausdrücke; HTML mit regulären Ausdrücken sicher zu filtern, ist in der Praxis nicht möglich. Verwenden Sie eine dafür entwickelte, gepflegte Bibliothek (etwa HTML Purifier in PHP oder DOMPurify im Browser).
strip_tagsallein reicht nicht aus: Die Attribute der von Ihnen erlaubten Tags bereinigt die Funktion nicht.- Führen Sie die Bereinigung auf dem Server durch; verlassen Sie sich nicht auf das HTML, das der Editor im Browser erzeugt.
- Lassen Sie in Links nur die Schemata
http,httpsundmailtozu.
Content-Security-Policy (CSP)
Die CSP ist ein Antwort-Header, der dem Browser mitteilt, aus welchen Quellen die Seite Skripte, Stylesheets und Bilder laden darf. Eine Richtlinie, die Inline-Skripte nicht zulässt, verhindert, dass ein irgendwie in die Seite gelangtes Skript ausgeführt wird. Die CSP ersetzt das Escaping nicht; sie ist der Sicherheitsgurt, der greift, wenn ein Fehler passiert.
Auf einer bestehenden Website kann eine strenge Richtlinie Seiten unbrauchbar machen. Beginnen Sie deshalb im Report-Only-Modus; es wird nichts blockiert, Verstöße erscheinen lediglich in der Browserkonsole und, sofern Sie eine definiert haben, an der Berichtsadresse. Erst wenn Sie die Verstöße behoben haben, setzen Sie die Richtlinie durch.
<IfModule mod_headers.c>
# Phase 1: nur melden, nichts blockieren
Header always set Content-Security-Policy-Report-Only "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; object-src 'none'; base-uri 'self'; frame-ancestors 'self'"
# Phase 2: Nachdem die Verstöße behoben sind, den Namen des Headers
# in Content-Security-Policy ändern
</IfModule>Inline-Skripte in eigene Dateien auszulagern, ist der wichtigste Schritt, um die CSP zu verschärfen. Für Inline-Skripte, die sich nicht auslagern lassen, kann ein nonce-Wert verwendet werden, der sich bei jeder Anfrage ändert. Einzelheiten finden Sie in der Anleitung Sicherheitsheader und HTTPS.
Schützen Sie das Sitzungs-Cookie
Ist das Sitzungs-Cookie als HttpOnly gekennzeichnet, kann JavaScript es nicht auslesen; selbst bei einer XSS-Schwachstelle wird so verhindert, dass das Cookie direkt gestohlen wird. Das Flag Secure sorgt dafür, dass das Cookie nur über HTTPS gesendet wird, und SameSite schränkt ein, dass es bei Anfragen mitgesendet wird, die von anderen Websites ausgehen.
<?php
session_set_cookie_params([
'lifetime' => 0,
'path' => '/',
'secure' => true, // nur HTTPS
'httponly' => true, // für JavaScript nicht lesbar
'samesite' => 'Lax',
]);
session_start();HttpOnly verhindert die übrigen Auswirkungen von XSS (Aktionen im Namen des Benutzers, Veränderung des Seiteninhalts) nicht; die eigentliche Lösung bleibt das Escaping.
Checkliste
- Alle Variablenausgaben in den Templates laufen über die Escaping-Hilfsfunktion.
- Attributwerte stehen in Anführungszeichen.
- Daten werden mit
json_encodeoder über eindata--Attribut an JavaScript übergeben. - Bei Adressen, die vom Benutzer stammen, wird das Schema geprüft.
- In JavaScript werden Benutzerdaten mit
textContentgeschrieben; die Verwendungen voninnerHTMLwurden durchgesehen. - Rich Text wird auf dem Server mit einer Bibliothek bereinigt, die mit einer Positivliste arbeitet.
- Die CSP ist mindestens im Report-Only-Modus gesetzt.
- Das Sitzungs-Cookie wird mit den Flags HttpOnly, Secure und SameSite ausgegeben.
- Auch Benutzereingaben, die im Administrationsbereich aufgelistet werden (Formularanfragen, Kommentare), werden escapt.
Häufige Fragen
Muss ich bei der Ausgabe escapen, wenn ich die Eingabe schon beim Speichern bereinige?
Ja. Der Schutz erfolgt bei der Ausgabe, denn derselbe Wert kann in unterschiedlichen Kontexten verwendet werden, und Daten können auch auf anderen Wegen in die Datenbank gelangen. Prüfen Sie bei der Eingabe nur das Format und überlassen Sie das Escaping der Ausgabe.
Verhindert strip_tags XSS?
Für sich allein ist die Funktion nicht verlässlich. Im Attributkontext hilft sie nicht, und die Attribute erlaubter Tags bereinigt sie nicht. Verwenden Sie in reinen Textfeldern htmlspecialchars und bei Rich Text eine Bereinigungsbibliothek mit Positivliste.
Kann ich auf das Escaping verzichten, wenn ich eine CSP einrichte?
Nein. Die CSP ist die zweite Verteidigungslinie; sie kann durch alte Browser, eine fehlerhafte Konfiguration oder über erlaubte Quellen umgangen werden. Der eigentliche Schutz ist korrektes Escaping.
Ist Escaping auch auf Seiten nötig, die nur Administratoren sehen?
Ja, gerade dort. Felder, die jeder ausfüllen kann, etwa ein Kontaktformular oder Kommentare, werden im Administrationsbereich aufgelistet; ohne Escaping läuft das Skript in der Sitzung des Administrators.
BYK Yazılım Support-Team
Diese Anleitung wird vom Support-Team von BYK Yazılım erstellt und regelmäßig überprüft. Letzte Aktualisierung: 04.10.2026.
Verwandte Anleitungen
- CSRF: Was ist das und wie verhindern Sie es?CSRF-Token, SameSite-Cookie, Origin-Prüfung und POST für alle Aktionen, die einen Zustand ändern.
- Sicherheitsheader und HTTPSHSTS, CSP, X-Content-Type-Options, Referrer-Policy und Permissions-Policy; mit .htaccess-Beispiel.
- Login- und SitzungssicherheitPasswort-Hashing, Begrenzung der Anmeldeversuche, Session-Fixation, Cookie-Flags und Berechtigungsprüfung bei jeder Anfrage.
- SQL-Injection: Was ist das und wie verhindern Sie sie?Prepared Statements, Positivliste, Datenbankbenutzer mit geringsten Rechten und das Verbergen von Fehlermeldungen.
Sehen wir uns Ihre Website gemeinsam an
BYK Yazılım entwickelt Unternehmenswebsites. Schreiben Sie uns, wenn Sie Fragen zu Ihrer Website haben.
Kontakt aufnehmen Unsere Leistung: Unternehmenswebsite