CSRF-Schutz: Wie du deine Webanwendungen effektiv vor Angriffen schützt
Präventionsmaßnahmen
Hauptschutzmaßnahme: CSRF-Token
Input Validation
Output Encoding / Escaping
Nutze Framework-Features
Befolge Security Best Practices
Einleitung
Cross-Site Request Forgery (CSRF) ist eine häufig auftretende Sicherheitslücke in Webanwendungen. Bei einem CSRF-Angriff wird der authentifizierte Nutzer dazu gebracht, unbeabsichtigt eine schädliche Aktion auszuführen, wie z.B. das Ändern des Passworts oder das Überweisen von Geld. Das geschieht, ohne dass der Nutzer davon etwas mitbekommt.
CSRF-Angriffe sind weit verbreitet und können schwerwiegende Folgen haben. Daher ist es äußerst wichtig, dass Entwickler ihre Webanwendungen konsequent gegen diese Art von Angriffen schützen. In diesem Tutorial lernst du, wie du deine Anwendungen wirksam vor CSRF-Angriffen schützt.
Wie funktioniert die Schwachstelle?
CSRF-Angriffe nutzen die Tatsache aus, dass Webbrowser automatisch alle Cookies, die zu einer bestimmten Domain gehören, mit jeder Anfrage an diese Domain mitsenden. Somit können Angreifer die Identität eines bereits angemeldeten Nutzers missbrauchen, um im Namen dieses Nutzers Aktionen auszuführen.
Hier ein Beispiel für eine anfällige Aktion:
<?php
// PHP-Beispiel für eine anfällige Aktion
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$newPassword = $_POST['new_password'];
$userId = $_SESSION['user_id'];
updatePassword($userId, $newPassword);
// Weiterleitung nach erfolgreicher Passwortänderung
header('Location: /account');
}
?>
<form method="POST" action="/change-password">
<label for="new_password">Neues Passwort:</label>
<input type="password" id="new_password" name="new_password" required>
<button type="submit">Passwort ändern</button>
</form>
In diesem Beispiel kann ein Angreifer eine manipulierte Webseite erstellen, die im Hintergrund eine Anfrage an /change-password sendet. Wenn der Nutzer auf dieser Seite eingeloggt ist, werden seine gültigen Sitzungscookies automatisch mit der Anfrage übertragen, sodass der Angreifer die Passwortänderung im Namen des Nutzers durchführen kann.
Präventionsmaßnahmen
Um deine Anwendung vor CSRF-Angriffen zu schützen, musst du mehrere Sicherheitsmaßnahmen umsetzen:
-
Hauptschutzmaßnahme: CSRF-Token
- Generiere für jede relevante Aktion einen eindeutigen, zufälligen Token.
- Übertrage diesen Token mit jeder Formularsendung und überprüfe ihn auf dem Server.
- Stelle sicher, dass der Token in der Sitzung des Nutzers gespeichert wird und nur dieser Nutzer ihn kennt. -
Input Validation
- Überprüfe sorgfältig alle Benutzereingaben, bevor du sie verarbeitest.
- Entferne oder kodiere gefährliche Zeichen, um Cross-Site Scripting (XSS) zu verhindern. -
Output Encoding / Escaping
- Wandle alle Benutzereingaben in sichere HTML-Entities um, bevor du sie auf der Webseite ausgiebst.
- Das verhindert, dass Angreifer über Benutzereingaben schädlichen Code einschleusen können. -
Nutze Framework-Features
- Viele moderne Webframeworks bieten integrierte CSRF-Schutzfunktionen an.
- Informiere dich über die CSRF-Schutzmaßnahmen deines Frameworks und nutze sie konsequent. -
Befolge Security Best Practices
- Sichere deine Sitzungen durch HttpOnly- und Secure-Flags ab.
- Verwende HTTPS für die gesamte Kommunikation.
- Implementiere eine strikte Content Security Policy (CSP).
Code-Beispiele
Unsicherer Code (was nicht zu tun ist)
<?php
// PHP-Beispiel für eine anfällige Aktion
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$newPassword = $_POST['new_password'];
$userId = $_SESSION['user_id'];
updatePassword($userId, $newPassword);
header('Location: /account');
}
?>
<form method="POST" action="/change-password">
<label for="new_password">Neues Passwort:</label>
<input type="password" id="new_password" name="new_password" required>
<button type="submit">Passwort ändern</button>
</form>
Sicherer Code (Best Practice)
<?php
// PHP-Beispiel für sichere CSRF-Implementierung
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token = $_POST['csrf_token'];
if (!validateCSRFToken($token, $_SESSION['csrf_token'])) {
http_response_code(403);
exit('Ungültiger CSRF-Token');
}
$newPassword = $_POST['new_password'];
$userId = $_SESSION['user_id'];
updatePassword($userId, $newPassword);
header('Location: /account');
}
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
?>
<form method="POST" action="/change-password">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<label for="new_password">Neues Passwort:</label>
<input type="password" id="new_password" name="new_password" required>
<button type="submit">Passwort ändern</button>
</form>
// JavaScript-Beispiel für sichere CSRF-Implementierung
function fetchWithCSRF(url, options) {
const token = getCookie('csrf_token');
if (!token) {
throw new Error('CSRF-Token fehlt');
}
return fetch(url, {
...options,
headers: {
...options.headers,
'X-CSRF-Token': token
}
});
}
function getCookie(name) {
const cookies = document.cookie.split(';');
for (const cookie of cookies) {
const [key, value] = cookie.trim().split('=');
if (key === name) {
return value;
}
}
return null;
}
Defensive Zusatzmaßnahmen
Neben den zuvor beschriebenen Schutzmaßnahmen gibt es weitere Möglichkeiten, deine Anwendung vor CSRF-Angriffen zu schützen:
-
Web Application Firewall (WAF)
- Eine WAF kann CSRF-Angriffe erkennen und blocken, bevor sie deine Anwendung erreichen.
- Viele WAFs bieten integrierte CSRF-Schutzfunktionen an. -
Monitoring und Detection
- Überwache deine Anwendung auf verdächtige Aktivitäten, die auf CSRF-Angriffe hinweisen können.
- Nutze Security-Monitoring-Tools, um Anomalien frühzeitig zu erkennen. -
Security Testing
- Führe regelmäßig CSRF-Penetrationstests durch, um die Wirksamkeit deiner Schutzmaßnahmen zu überprüfen.
- Lass deine Anwendung von Sicherheitsexperten testen. -
Code Review Checkliste
- Stelle sicher, dass dein Team bei der Entwicklung stets die CSRF-Schutzmaßnahmen berücksichtigt.
- Integriere CSRF-Sicherheit in deine Code-Review-Checkliste.
Zusammenfassung
Die wichtigsten Punkte zum Schutz vor CSRF-Angriffen sind:
- Verwende CSRF-Token für alle sicherheitskritischen Aktionen.
- Validiere und kodiere alle Benutzereingaben sorgfältig.
- Nutze die integrierten CSRF-Schutzfunktionen deines Webframeworks.
- Befolge allgemeine Web-Sicherheitsempfehlungen wie HTTPS und Content Security Policy.
- Setze zusätzliche Sicherheitsmaßnahmen wie WAF, Monitoring und Penetrationstests ein.
Achte bei der Entwicklung deiner Webanwendungen immer auf CSRF-Sicherheit. Nur so kannst du deine Nutzer und deine Anwendung effektiv vor schwerwiegenden Angriffen schützen.
Weiterführende Ressourcen
- OWASP Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet
- CWE-352: Cross-Site Request Forgery (CSRF)
- Offizielle PHP-Dokumentation zu CSRF-Schutz
CVE-Beispiele
- CVE-2018-7600 (Drupal): Ein CSRF-Fehler in Drupal ermöglichte es Angreifern, beliebigen Code auf dem Server auszuführen. Das führte zu einer Reihe schwerwiegender Sicherheitsvorfälle.
- CVE-2020-8958 (Magento): Eine CSRF-Lücke in Magento erlaubte es Angreifern, Administratorkonten zu übernehmen und den gesamten Shop zu kompromittieren.
- CVE-2021-32648 (Joomla): Ein CSRF-Fehler in Joomla ermöglichte es Angreifern, beliebige Dateien auf dem Server hochzuladen und auszuführen.
Diese Beispiele zeigen, wie schwerwiegend die Folgen von CSRF-Schwachstellen sein können. Daher ist es unerlässlich, dass Entwickler ihre Anwendungen konsequent gegen diese Art von Angriffen schützen.
Code-Beispiele
Hier ein Beispiel für eine anfällige Aktion:
<?php
// PHP-Beispiel für eine anfällige Aktion
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$newPassword = $_POST['new_password'];
$userId = $_SESSION['user_id'];
updatePassword($userId, $newPassword);
// Weiterleitung nach erfolgreicher Passwortänderung
header('Location: /account');
}
?>
<form method="POST" action="/change-password">
<label for="new_password">Neues Passwort:</label>
<input type="password" id="new_password" name="new_password" required>
<button type="submit">Passwort ändern</button>
</form>
### Unsicherer Code (was nicht zu tun ist)
<?php
// PHP-Beispiel für eine anfällige Aktion
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$newPassword = $_POST['new_password'];
$userId = $_SESSION['user_id'];
updatePassword($userId, $newPassword);
header('Location: /account');
}
?>
<form method="POST" action="/change-password">
<label for="new_password">Neues Passwort:</label>
<input type="password" id="new_password" name="new_password" required>
<button type="submit">Passwort ändern</button>
</form>
### Sicherer Code (Best Practice)
<?php
// PHP-Beispiel für sichere CSRF-Implementierung
session_start();
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$token = $_POST['csrf_token'];
if (!validateCSRFToken($token, $_SESSION['csrf_token'])) {
http_response_code(403);
exit('Ungültiger CSRF-Token');
}
$newPassword = $_POST['new_password'];
$userId = $_SESSION['user_id'];
updatePassword($userId, $newPassword);
header('Location: /account');
}
$_SESSION['csrf_token'] = bin2hex(random_bytes(32));
?>
<form method="POST" action="/change-password">
<input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
<label for="new_password">Neues Passwort:</label>
<input type="password" id="new_password" name="new_password" required>
<button type="submit">Passwort ändern</button>
</form>
```
// JavaScript-Beispiel für sichere CSRF-Implementierung
function fetchWithCSRF(url, options) {
const token = getCookie('csrf_token');
if (!token) {
throw new Error('CSRF-Token fehlt');
}
return fetch(url, {
...options,
headers: {
...options.headers,
'X-CSRF-Token': token
}
});
}
function getCookie(name) {
const cookies = document.cookie.split(';');
for (const cookie of cookies) {
const [key, value] = cookie.trim().split('=');
if (key === name) {
return value;
}
}
return null;
}