In diesem Abschnitt beschreiben wir einige allgemeine Prinzipien zur Verhinderung von Cross-Site-Scripting-Schwachstellen und Möglichkeiten zur Verwendung verschiedener gängiger Technologien zum Schutz vor XSS-Angriffen.
Cross-Site-Scripting-Prävention kann im Allgemeinen über zwei Verteidigungsebenen erreicht werden:
- Codieren Sie Daten bei der Ausgabe
- Validieren Sie die Eingabe bei der Ankunft
Sie können Burp Scanner verwenden, um Ihre Websites auf zahlreiche Sicherheitslücken, einschließlich XSS, zu scannen. Burps hochmoderne Scan-Logik repliziert die Aktionen eines erfahrenen Angreifers und ist in der Lage, eine entsprechend hohe Abdeckung von XSS-Schwachstellen zu erreichen. Sie können Burp Scanner verwenden, um sicherzustellen, dass Ihre Abwehr gegen XSS-Angriffe effektiv funktioniert.
Daten bei der Ausgabe codieren
Die Codierung sollte direkt angewendet werden, bevor vom Benutzer steuerbare Daten auf eine Seite geschrieben werden, da der Kontext, in den Sie schreiben, bestimmt, welche Art von Codierung Sie verwenden müssen. Beispielsweise erfordern Werte in einer JavaScript-Zeichenfolge eine andere Art von Escaping als in einem HTML-Kontext.
In einem HTML-Kontext sollten Sie Werte ohne Whitelist in HTML-Entitäten konvertieren:
-
<
konvertiert in:<
-
>
konvertiert in:>
In einem JavaScript-String-Kontext sollten nicht alphanumerische Werte Unicode-escaped sein:
-
<
konvertiert in:\u003c
-
>
konvertiert in:\u003e
Manchmal müssen Sie mehrere Codierungsebenen in der richtigen Reihenfolge anwenden. Um beispielsweise Benutzereingaben sicher in einen Ereignishandler einzubetten, müssen Sie sich sowohl mit dem JavaScript- als auch mit dem HTML-Kontext befassen. Sie müssen also zuerst die Eingabe Unicode-escape und dann HTML-codieren:
<a href="#" onclick="x='This string needs two layers of escaping'">test</a>
Validate input on arrival
Die Codierung ist wahrscheinlich die wichtigste XSS-Verteidigungslinie, reicht jedoch nicht aus, um XSS-Schwachstellen in jedem Kontext zu verhindern. Sie sollten die Eingabe auch so genau wie möglich an dem Punkt validieren, an dem sie zum ersten Mal von einem Benutzer empfangen wird.
Beispiele für die Eingabevalidierung sind:
- Wenn ein Benutzer eine URL sendet, die in Antworten zurückgegeben wird, überprüfen Sie, ob sie mit einem sicheren Protokoll wie HTTP und HTTPS beginnt. Andernfalls könnte jemand Ihre Website mit einem schädlichen Protokoll wie
javascript
oderdata
ausnutzen. - Wenn ein Benutzer einen erwarteten numerischen Wert angibt, wird überprüft, ob der Wert tatsächlich eine Ganzzahl enthält.
- Überprüfen, ob die Eingabe nur einen erwarteten Satz von Zeichen enthält.
Die Eingabevalidierung sollte idealerweise funktionieren, indem ungültige Eingaben blockiert werden. Ein alternativer Ansatz, ungültige Eingaben zu bereinigen, um sie gültig zu machen, ist fehleranfälliger und sollte nach Möglichkeit vermieden werden.
Whitelisting vs. Blacklisting
Die Eingabevalidierung sollte im Allgemeinen eher Whitelists als Blacklists verwenden. Anstatt beispielsweise zu versuchen, eine Liste aller schädlichen Protokolle zu erstellen (javascript
, data
usw.), erstellen Sie einfach eine Liste sicherer Protokolle (HTTP, HTTPS) und verbieten Sie alles, was nicht auf der Liste steht. Dies stellt sicher, dass Ihre Verteidigung nicht bricht, wenn neue schädliche Protokolle erscheinen, und macht sie weniger anfällig für Angriffe, die versuchen, ungültige Werte zu verschleiern, um einer Blacklist auszuweichen.
Zulassen von „sicherem“ HTML
Das Zulassen von Benutzern zum Posten von HTML-Markup sollte nach Möglichkeit vermieden werden, ist jedoch manchmal eine Geschäftsanforderung. Auf einer Blog-Site können beispielsweise Kommentare gepostet werden, die ein begrenztes HTML-Markup enthalten.
Der klassische Ansatz besteht darin, potenziell schädliche Tags und JavaScript herauszufiltern. Sie können versuchen, dies mithilfe einer Whitelist mit sicheren Tags und Attributen zu implementieren, aber dank Diskrepanzen in den Browser-Parsing-Engines und Macken wie Mutation XSS ist dieser Ansatz äußerst schwierig sicher zu implementieren.
Die am wenigsten schlechte Option ist die Verwendung einer JavaScript-Bibliothek, die im Browser des Benutzers filtert und codiert, z. B. DOMPurify. Andere Bibliotheken ermöglichen es Benutzern, Inhalte im Markdown-Format bereitzustellen und den Markdown in HTML zu konvertieren. Leider weisen alle diese Bibliotheken von Zeit zu Zeit XSS-Schwachstellen auf, sodass dies keine perfekte Lösung ist. Wenn Sie eines verwenden, sollten Sie es genau auf Sicherheitsupdates überwachen.
Hinweis
Zusätzlich zu JavaScript können andere Inhalte wie CSS und sogar normales HTML in einigen Situationen schädlich sein.
Angriffe mit bösartigem CSS
So verhindern Sie XSS mithilfe einer Template-Engine
Viele moderne Websites verwenden serverseitige Template-Engines wie Twig und Freemarker, um dynamische Inhalte in HTML einzubetten. Diese definieren typischerweise ihr eigenes Fluchtsystem. In Twig können Sie beispielsweise den Filter e()
mit einem Argument verwenden, das den Kontext definiert:
{{ user.firstname | e('html') }}
Einige andere Template-Engines, wie Jinja und React, umgehen standardmäßig dynamischen Inhalt, wodurch die meisten XSS-Vorkommen effektiv verhindert werden.
Wir empfehlen, die Escaping-Funktionen genau zu überprüfen, wenn Sie prüfen, ob Sie eine bestimmte Template-Engine oder ein bestimmtes Framework verwenden.
Hinweis
Wenn Sie Benutzereingaben direkt in Vorlagenzeichenfolgen verketten, sind Sie anfällig für die serverseitige Vorlageninjektion, die häufig schwerwiegender ist als XSS.
So verhindern Sie XSS in PHP
In PHP gibt es eine integrierte Funktion zum Codieren von Entitäten namens htmlentities
. Sie sollten diese Funktion aufrufen, um Ihre Eingabe in einem HTML-Kontext zu umgehen. Die Funktion sollte mit drei Argumenten aufgerufen werden:
- Ihre Eingabezeichenfolge.
-
ENT_QUOTES
, welches ist ein Flag, das angibt, dass alle Anführungszeichen codiert werden sollen. - Der Zeichensatz, der in den meisten Fällen UTF-8 sein sollte.
Zum Beispiel:
<?php echo htmlentities($input, ENT_QUOTES, 'UTF-8');?>
Wenn Sie sich in einem JavaScript-String-Kontext befinden, müssen Sie die Eingabe wie bereits beschrieben Unicode-escape . Leider bietet PHP keine API für Unicode-Escape-Zeichenfolgen. Hier ist ein Code, um das in PHP zu tun:
<?php
function jsEscape($str) {
$output = '';
$str = str_split($str);
for($i=0;$i<count($str);$i++) {
$chrNum = ord($str);
$chr = $str;
if($chrNum === 226) {
if(isset($str) && ord($str) === 128) {
if(isset($str) && ord($str) === 168) {
$output .= '\u2028';
$i += 2;
continue;
}
if(isset($str) && ord($str) === 169) {
$output .= '\u2029';
$i += 2;
continue;
}
}
}
switch($chr) {
case "'":
case '"':
case "\n";
case "\r";
case "&";
case "\";
case "<":
case ">":
$output .= sprintf("\u%04x", $chrNum);
break;
default:
$output .= $str;
break;
}
}
return $output;
}
?>
So verwenden Sie die Funktion jsEscape
in PHP:
<script>x = '<?php echo jsEscape($_GET)?>';</script>
Alternativ können Sie eine Template-Engine verwenden.
So verhindern Sie XSS clientseitig in JavaScript
Um Benutzereingaben in einem HTML-Kontext in JavaScript zu umgehen, benötigen Sie einen eigenen HTML-Encoder, da JavaScript keine API zum Codieren von HTML bereitstellt. Hier ist ein Beispiel für JavaScript-Code, der eine Zeichenfolge in HTML-Entitäten konvertiert:
function htmlEncode(str){
return String(str).replace(//gi, function(c){
return '&#'+c.charCodeAt(0)+';';
});
}
Sie würden diese Funktion dann wie folgt verwenden:
<script>document.body.innerHTML = htmlEncode(untrustedValue)</script>
Wenn sich Ihre Eingabe in einer JavaScript-Zeichenfolge befindet, benötigen Sie einen Encoder, der Unicode-Escaping ausführt. Hier ist ein Beispiel-Unicode-Encoder:
function jsEscape(str){
return String(str).replace(//gi, function(c){
return '\u'+('0000'+c.charCodeAt(0).toString(16)).slice(-4);
});
}
Sie würden diese Funktion dann wie folgt verwenden:
<script>document.write('<script>x="'+jsEscape(untrustedValue)+'";<\/script>')</script>
So verhindern Sie XSS in jQuery
Die häufigste Form von XSS in jQuery ist, wenn Sie Benutzereingaben an einen jQuery-Selektor übergeben. Webentwickler verwendeten häufig location.hash
und übergaben es an den Selektor, der XSS verursachen würde, da jQuery den HTML-Code rendern würde. jQuery hat dieses Problem erkannt und seine Selektorlogik gepatcht, um zu überprüfen, ob die Eingabe mit einem Hash beginnt. Jetzt rendert jQuery HTML nur, wenn das erste Zeichen ein <
. Wenn Sie nicht vertrauenswürdige Daten an den jQuery-Selektor übergeben, stellen Sie sicher, dass Sie den Wert mithilfe der obigen Funktion jsEscape
korrekt maskieren.
Minderung von XSS mithilfe von Content Security Policy (CSP)
Content Security Policy (CSP) ist die letzte Verteidigungslinie gegen Cross-Site Scripting. Wenn Ihre XSS-Prävention fehlschlägt, können Sie CSP verwenden, um XSS zu mildern, indem Sie einschränken, was ein Angreifer tun kann.
Mit CSP können Sie verschiedene Dinge steuern, z. B. ob externe Skripte geladen werden können und ob Inline-Skripte ausgeführt werden. Um CSP bereitzustellen, müssen Sie einen HTTP-Antwortheader mit dem Namen Content-Security-Policy
mit einem Wert einschließen, der Ihre Richtlinie enthält.
Ein Beispiel-CSP lautet wie folgt:
default-src 'self'; script-src 'self'; object-src 'none'; frame-src 'none'; base-uri 'none';
Diese Richtlinie legt fest, dass Ressourcen wie Bilder und Skripte nur vom selben Ursprung wie die Hauptseite geladen werden können. Selbst wenn ein Angreifer eine XSS-Nutzlast erfolgreich injizieren kann, kann er nur Ressourcen vom aktuellen Ursprung laden. Dadurch wird die Wahrscheinlichkeit, dass ein Angreifer die XSS-Sicherheitsanfälligkeit ausnutzt, erheblich verringert.
Wenn Sie externe Ressourcen laden müssen, stellen Sie sicher, dass Sie nur Skripte zulassen, die einem Angreifer nicht helfen, Ihre Site auszunutzen. Wenn Sie beispielsweise bestimmte Domänen auf die Whitelist setzen, kann ein Angreifer jedes Skript aus diesen Domänen laden. Versuchen Sie nach Möglichkeit, Ressourcen auf Ihrer eigenen Domain zu hosten.
Wenn dies nicht möglich ist, können Sie Hash- oder Nonce-basierte Richtlinien verwenden, um Skripte für verschiedene Domänen zuzulassen. Eine Nonce ist eine zufällige Zeichenfolge, die als Attribut eines Skripts oder einer Ressource hinzugefügt wird und nur ausgeführt wird, wenn die zufällige Zeichenfolge mit der vom Server generierten übereinstimmt. Ein Angreifer kann die randomisierte Zeichenfolge nicht erraten und kann daher kein Skript oder keine Ressource mit einer gültigen nonce aufrufen.