← Zurück zum Blog

JSON Web Tokens (JWT): Kompletter Leitfaden für sichere Authentifizierung

Lernen Sie alles über JSON Web Tokens (JWT). Struktur, Signierung, Validierung und Best Practices für sichere Authentifizierung in modernen Anwendungen.

Big JSON Team15 Min. Lesezeitsecurity
B

Big JSON Team

Technical Writer

Expert in JSON data manipulation, API development, and web technologies. Passionate about creating tools that make developers' lives easier.

15 Min. Lesezeit

# JSON Web Tokens (JWT): Kompletter Leitfaden

JSON Web Tokens (JWT) sind der de-facto Standard für sichere Authentifizierung in modernen Web-Anwendungen. Dieser Leitfaden erklärt alles, was Sie wissen müssen.

Was ist ein JWT?

Ein JWT ist ein kompakter, URL-sicherer Token, der verwendet wird, um Informationen zwischen zwei Parteien sicher zu übertragen.

Beispiel eines JWT

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik1heCBNdXN0ZXJtYW5uIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

JWT-Struktur

Ein JWT besteht aus drei Teilen, getrennt durch Punkte (.):

HEADER.PAYLOAD.SIGNATURE

1. Header

Der Header enthält Metadaten über den Token:

{

"alg": "HS256",

"typ": "JWT"

}

  • alg: Algorithmus (z.B. HS256, RS256)
  • typ: Token-Typ (immer JWT)

Base64URL-kodiert: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9

2. Payload

Der Payload enthält die Claims (Behauptungen):

{

"sub": "1234567890",

"name": "Max Mustermann",

"email": "max@example.com",

"iat": 1516239022,

"exp": 1516242622

}

Base64URL-kodiert: eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6Ik1heCBNdXN0ZXJtYW5uIiwiaWF0IjoxNTE2MjM5MDIyfQ

3. Signature

Die Signatur stellt sicher, dass der Token nicht manipuliert wurde:

HMACSHA256(

base64UrlEncode(header) + "." + base64UrlEncode(payload),

secret

)

Standard Claims

Registered Claims

iss (Issuer) - Aussteller des Tokens
{"iss": "https://auth.example.com"}
sub (Subject) - Subjekt des Tokens (meist User-ID)
{"sub": "user123"}
aud (Audience) - Empfänger des Tokens
{"aud": "https://api.example.com"}
exp (Expiration Time) - Ablaufzeit (Unix Timestamp)
{"exp": 1706265600}
nbf (Not Before) - Gültig ab (Unix Timestamp)
{"nbf": 1706262000}
iat (Issued At) - Ausstellungszeit
{"iat": 1706262000}
jti (JWT ID) - Eindeutige Token-ID
{"jti": "abc123xyz"}

Custom Claims

Sie können eigene Claims hinzufügen:

{

"sub": "user123",

"name": "Max Mustermann",

"email": "max@example.com",

"roles": ["admin", "user"],

"department": "IT",

"customData": {

"preference": "dark-mode"

}

}

JWT erstellen und validieren

Node.js mit jsonwebtoken

const jwt = require('jsonwebtoken');

// Secret Key (sollte in Umgebungsvariablen gespeichert werden)

const SECRET_KEY = process.env.JWT_SECRET || 'your-secret-key';

// JWT erstellen

function createToken(userId, email) {

const payload = {

sub: userId,

email: email,

iat: Math.floor(Date.now() / 1000),

exp: Math.floor(Date.now() / 1000) + (60 * 60) // 1 Stunde

};

return jwt.sign(payload, SECRET_KEY, {

algorithm: 'HS256'

});

}

// JWT validieren

function verifyToken(token) {

try {

const decoded = jwt.verify(token, SECRET_KEY);

return {

valid: true,

data: decoded

};

} catch (error) {

return {

valid: false,

error: error.message

};

}

}

// Verwendung

const token = createToken('user123', 'max@example.com');

console.log('Token:', token);

const result = verifyToken(token);

if (result.valid) {

console.log('Token ist gültig:', result.data);

} else {

console.log('Token ungültig:', result.error);

}

Python mit PyJWT

import jwt

import datetime

import os

SECRET_KEY = os.getenv('JWT_SECRET', 'your-secret-key')

def create_token(user_id: str, email: str) -> str:

"""Erstelle JWT"""

payload = {

'sub': user_id,

'email': email,

'iat': datetime.datetime.utcnow(),

'exp': datetime.datetime.utcnow() + datetime.timedelta(hours=1)

}

return jwt.encode(payload, SECRET_KEY, algorithm='HS256')

def verify_token(token: str) -> dict:

"""Validiere JWT"""

try:

decoded = jwt.decode(token, SECRET_KEY, algorithms=['HS256'])

return {'valid': True, 'data': decoded}

except jwt.ExpiredSignatureError:

return {'valid': False, 'error': 'Token abgelaufen'}

except jwt.InvalidTokenError as e:

return {'valid': False, 'error': str(e)}

# Verwendung

token = create_token('user123', 'max@example.com')

print(f"Token: {token}")

result = verify_token(token)

if result['valid']:

print(f"Token gültig: {result['data']}")

else:

print(f"Token ungültig: {result['error']}")

Signatur-Algorithmen

Symmetric (HMAC)

HS256, HS384, HS512 - Shared Secret
// Gleicher Secret für Signierung und Validierung

const token = jwt.sign(payload, 'secret-key', { algorithm: 'HS256' });

const decoded = jwt.verify(token, 'secret-key');

✅ Vorteile:

  • Schnell
  • Einfach zu implementieren

⚠️ Nachteile:

  • Secret muss geteilt werden
  • Nur geeignet für vertrauenswürdige Umgebungen

Asymmetric (RSA/ECDSA)

RS256, RS384, RS512 - RSA ES256, ES384, ES512 - ECDSA
const fs = require('fs');

// Private Key für Signierung

const privateKey = fs.readFileSync('private.key');

const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });

// Public Key für Validierung

const publicKey = fs.readFileSync('public.key');

const decoded = jwt.verify(token, publicKey);

✅ Vorteile:

  • Public Key kann sicher geteilt werden
  • Besser für verteilte Systeme

⚠️ Nachteile:

  • Langsamer als HMAC
  • Komplexere Key-Verwaltung

Praktische Implementierung

Express.js Middleware

const express = require('express');

const jwt = require('jsonwebtoken');

const app = express();

const SECRET_KEY = process.env.JWT_SECRET;

// Middleware für JWT-Authentifizierung

function authenticateToken(req, res, next) {

// Token aus Header extrahieren

const authHeader = req.headers['authorization'];

const token = authHeader && authHeader.split(' ')[1]; // "Bearer TOKEN"

if (!token) {

return res.status(401).json({ error: 'Kein Token bereitgestellt' });

}

jwt.verify(token, SECRET_KEY, (err, user) => {

if (err) {

return res.status(403).json({ error: 'Token ungültig oder abgelaufen' });

}

req.user = user;

next();

});

}

// Login-Route

app.post('/api/login', (req, res) => {

const { username, password } = req.body;

// Benutzer authentifizieren (Pseudo-Code)

const user = authenticateUser(username, password);

if (!user) {

return res.status(401).json({ error: 'Ungültige Anmeldedaten' });

}

// JWT erstellen

const token = jwt.sign(

{

sub: user.id,

username: user.username,

roles: user.roles

},

SECRET_KEY,

{ expiresIn: '1h' }

);

res.json({ token });

});

// Geschützte Route

app.get('/api/profile', authenticateToken, (req, res) => {

// req.user enthält die dekodierten Token-Daten

res.json({

message: 'Geschützte Daten',

user: req.user

});

});

app.listen(3000);

FastAPI (Python)

from fastapi import FastAPI, Depends, HTTPException, status

from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials

import jwt

from datetime import datetime, timedelta

from pydantic import BaseModel

app = FastAPI()

security = HTTPBearer()

SECRET_KEY = "your-secret-key"

class LoginRequest(BaseModel):

username: str

password: str

class TokenResponse(BaseModel):

access_token: str

token_type: str = "bearer"

def create_access_token(data: dict, expires_delta: timedelta = None):

to_encode = data.copy()

if expires_delta:

expire = datetime.utcnow() + expires_delta

else:

expire = datetime.utcnow() + timedelta(hours=1)

to_encode.update({"exp": expire})

encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm="HS256")

return encoded_jwt

def verify_token(credentials: HTTPAuthorizationCredentials = Depends(security)):

try:

payload = jwt.decode(

credentials.credentials,

SECRET_KEY,

algorithms=["HS256"]

)

return payload

except jwt.ExpiredSignatureError:

raise HTTPException(

status_code=status.HTTP_401_UNAUTHORIZED,

detail="Token abgelaufen"

)

except jwt.InvalidTokenError:

raise HTTPException(

status_code=status.HTTP_401_UNAUTHORIZED,

detail="Ungültiger Token"

)

@app.post("/api/login", response_model=TokenResponse)

async def login(request: LoginRequest):

# Benutzer authentifizieren (Pseudo-Code)

if not authenticate_user(request.username, request.password):

raise HTTPException(

status_code=status.HTTP_401_UNAUTHORIZED,

detail="Ungültige Anmeldedaten"

)

token = create_access_token(

data={"sub": request.username},

expires_delta=timedelta(hours=1)

)

return TokenResponse(access_token=token)

@app.get("/api/profile")

async def get_profile(user_data = Depends(verify_token)):

return {

"message": "Geschützte Daten",

"user": user_data

}

Refresh Tokens

Access Token + Refresh Token Pattern

const jwt = require('jsonwebtoken');

const ACCESS_TOKEN_SECRET = process.env.ACCESS_TOKEN_SECRET;

const REFRESH_TOKEN_SECRET = process.env.REFRESH_TOKEN_SECRET;

function generateTokens(userId) {

// Kurzlebiger Access Token (15 Minuten)

const accessToken = jwt.sign(

{ sub: userId },

ACCESS_TOKEN_SECRET,

{ expiresIn: '15m' }

);

// Langlebiger Refresh Token (7 Tage)

const refreshToken = jwt.sign(

{ sub: userId },

REFRESH_TOKEN_SECRET,

{ expiresIn: '7d' }

);

return { accessToken, refreshToken };

}

// Refresh-Endpoint

app.post('/api/token/refresh', (req, res) => {

const { refreshToken } = req.body;

if (!refreshToken) {

return res.status(401).json({ error: 'Refresh Token erforderlich' });

}

try {

const decoded = jwt.verify(refreshToken, REFRESH_TOKEN_SECRET);

// Neuen Access Token generieren

const newAccessToken = jwt.sign(

{ sub: decoded.sub },

ACCESS_TOKEN_SECRET,

{ expiresIn: '15m' }

);

res.json({ accessToken: newAccessToken });

} catch (error) {

return res.status(403).json({ error: 'Ungültiger Refresh Token' });

}

});

Sicherheits-Best Practices

1. Sichere Secret Keys

// SCHLECHT - Hardcoded Secret

const SECRET = 'my-secret';

// GUT - Umgebungsvariable

const SECRET = process.env.JWT_SECRET;

// Generiere starken Secret:

// node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"

2. Token-Ablaufzeit setzen

// Kurze Lebenszeit für Access Tokens

jwt.sign(payload, secret, { expiresIn: '15m' });

// Längere Lebenszeit für Refresh Tokens

jwt.sign(payload, secret, { expiresIn: '7d' });

3. HTTPS verwenden

// Immer über HTTPS übertragen

https://api.example.com/login

4. Sensitive Daten nicht im Payload

// SCHLECHT

const payload = {

sub: userId,

password: 'secret123', // ❌ NIEMALS!

creditCard: '1234-5678' // ❌ NIEMALS!

};

// GUT

const payload = {

sub: userId,

roles: ['user'],

email: 'user@example.com'

};

5. Token-Widerrufung implementieren

const redis = require('redis');

const client = redis.createClient();

// Token zur Blacklist hinzufügen

async function revokeToken(token) {

const decoded = jwt.decode(token);

const expiresIn = decoded.exp - Math.floor(Date.now() / 1000);

await client.setex(blacklist:\\${token}, expiresIn, 'revoked');

}

// Token-Blacklist prüfen

async function isTokenRevoked(token) {

const result = await client.get(blacklist:\\${token});

return result !== null;

}

// Middleware anpassen

async function authenticateToken(req, res, next) {

const token = extractToken(req);

if (await isTokenRevoked(token)) {

return res.status(403).json({ error: 'Token widerrufen' });

}

// ... normale Validierung

}

Debugging und Testing

JWT dekodieren (ohne Validierung)

const jwt = require('jsonwebtoken');

const token = 'eyJhbGc...';

// Dekodieren ohne Validierung

const decoded = jwt.decode(token, { complete: true });

console.log('Header:', decoded.header);

console.log('Payload:', decoded.payload);

console.log('Signature:', decoded.signature);

Online-Tools

  • jwt.io - JWT debuggen und testen
  • jwt-cli - Kommandozeilen-Tool

Unit-Tests

const jwt = require('jsonwebtoken');

const { expect } = require('chai');

describe('JWT Tests', () => {

const SECRET = 'test-secret';

it('sollte gültigen Token erstellen', () => {

const payload = { sub: 'user123' };

const token = jwt.sign(payload, SECRET);

expect(token).to.be.a('string');

expect(token.split('.')).to.have.lengthOf(3);

});

it('sollte Token validieren', () => {

const payload = { sub: 'user123' };

const token = jwt.sign(payload, SECRET);

const decoded = jwt.verify(token, SECRET);

expect(decoded.sub).to.equal('user123');

});

it('sollte abgelaufenen Token ablehnen', (done) => {

const payload = { sub: 'user123' };

const token = jwt.sign(payload, SECRET, { expiresIn: '1ms' });

setTimeout(() => {

expect(() => jwt.verify(token, SECRET)).to.throw();

done();

}, 10);

});

});

Zusammenfassung

JWT ist ein leistungsstarkes Tool für Authentifizierung, wenn es richtig eingesetzt wird:

Verwenden Sie:

  • Starke, zufällige Secret Keys
  • Kurze Ablaufzeiten für Access Tokens
  • HTTPS für alle Token-Übertragungen
  • Refresh Token Pattern
  • Token-Blacklisting für Logout

Vermeiden Sie:

  • Sensitive Daten im Payload
  • Lange Ablaufzeiten
  • Schwache Signatur-Algorithmen
  • Client-seitiges Speichern in localStorage (verwenden Sie httpOnly Cookies)

Mit diesen Best Practices können Sie JWT sicher in Ihren Anwendungen einsetzen.

Share:

Verwandte Artikel

Read in English