Open Redirect with Jwt Tokens
How Open Redirect Manifests in Jwt Tokens
Open redirect vulnerabilities in JWT-based systems often occur when authentication or authorization tokens contain redirect URLs that are processed without proper validation. In JWT implementations, this commonly appears in several specific contexts:
Post-authentication redirect attacks: After successful login, JWT tokens are issued and users are redirected to a URL specified in the token or as a parameter. Attackers can manipulate these redirects to send users to malicious sites.
// Vulnerable pattern in JWT auth middleware
app.post('/login', (req, res) => {
const token = jwt.sign({ userId: user.id }, secret, { expiresIn: '1h' });
const redirect = req.query.redirect || '/dashboard';
res.cookie('jwt', token, { httpOnly: true });
res.redirect(redirect); // <-- Vulnerable: no validation
});
Token claims containing URLs: Some JWT implementations store redirect URLs in token claims, which are later extracted and used without validation.
// Attacker-controlled redirect in token claims
const token = jwt.sign({
userId: user.id,
redirectUrl: req.body.redirectUrl // <-- User-controlled
}, secret, { expiresIn: '1h' });
API endpoint vulnerabilities: JWT-protected endpoints that accept URL parameters for redirects or resource references can be exploited.
// Vulnerable API endpoint
app.get('/api/redirect', authenticateJWT, (req, res) => {
const url = req.query.url; // <-- No validation
res.redirect(url);
});
Open redirect via JWT refresh tokens: Some systems use refresh tokens to obtain new access tokens and redirect users, creating another attack vector.
// Vulnerable refresh token flow
app.post('/refresh', (req, res) => {
const refreshToken = req.cookies.refreshToken;
const decoded = jwt.verify(refreshToken, refreshSecret);
const newToken = jwt.sign({ userId: decoded.userId }, secret);
const redirect = decoded.redirect || '/'; // <-- Could be attacker-controlled
res.cookie('jwt', newToken, { httpOnly: true });
res.redirect(redirect);
});
Jwt Tokens-Specific Detection
Detecting open redirect vulnerabilities in JWT implementations requires examining both the token structure and how URLs are processed. Here are Jwt Tokens-specific detection methods:
Static analysis of JWT claims: Scan JWT tokens for URL patterns in claims that might be used for redirects.
// Detect suspicious claims in JWT tokens
function scanJwtForRedirects(token) {
try {
const decoded = jwt.decode(token, { complete: true });
const suspiciousClaims = [];
Object.entries(decoded.payload).forEach(([key, value]) => {
if (typeof value === 'string' && isSuspiciousUrl(value)) {
suspiciousClaims.push({ claim: key, value });
}
});
return suspiciousClaims;
} catch (error) {
return null;
}
}
function isSuspiciousUrl(str) {
const urlPattern = /^https?:\/\/[^\s]+$/;
return urlPattern.test(str) &&
!str.startsWith('https://yourdomain.com');
}
Runtime redirect validation: Implement validation middleware that checks redirect URLs against a whitelist.
// JWT-aware redirect validation middleware
function validateRedirect(req, res, next) {
const redirect = req.query.redirect || req.body.redirect;
if (redirect) {
if (!isValidRedirect(redirect)) {
return res.status(400).json({
error: 'Invalid redirect URL'
});
}
}
next();
}
function isValidRedirect(url) {
const allowedDomains = ['https://yourdomain.com', 'https://app.yourdomain.com'];
try {
const parsed = new URL(url);
return allowedDomains.includes(`${parsed.protocol}//${parsed.host}`);
} catch {
return false;
}
}
middleBrick API scanning: Use middleBrick's black-box scanning to detect open redirect vulnerabilities in your JWT-protected endpoints. The scanner tests for redirect parameters and validates URL handling without requiring credentials.
# Scan JWT-protected endpoints with middleBrick
middlebrick scan https://api.yourservice.com/auth/redirect \
--test=open-redirect \
--jwt-secret=your-secret-key
The scanner tests common JWT redirect patterns and validates that URLs are properly sanitized before being used in redirects.
Jwt Tokens-Specific Remediation
Remediating open redirect vulnerabilities in JWT implementations requires a combination of validation strategies and secure coding practices. Here are Jwt Tokens-specific fixes:
URL validation and whitelisting: Always validate redirect URLs against a strict whitelist of allowed domains.
// Secure JWT redirect handling
const ALLOWED_DOMAINS = ['https://yourdomain.com', 'https://app.yourdomain.com'];
function secureRedirectWithJWT(req, res) {
const token = req.cookies.jwt;
try {
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const redirect = req.query.redirect || decoded.redirectUrl || '/dashboard';
if (!isValidRedirect(redirect)) {
return res.redirect('/dashboard');
}
res.redirect(redirect);
} catch (error) {
res.redirect('/login');
}
}
function isValidRedirect(url) {
try {
const parsed = new URL(url);
return ALLOWED_DOMAINS.includes(`${parsed.protocol}//${parsed.host}`);
} catch {
return false;
}
}
Claim sanitization: Sanitize JWT claims that contain URLs before using them in redirects.
// Sanitize JWT claims containing URLs
function sanitizeJwtClaims(claims) {
const sanitized = { ...claims };
Object.keys(sanitized).forEach(key => {
if (key.endsWith('Url') || key.endsWith('Redirect')) {
if (!isValidRedirect(sanitized[key])) {
sanitized[key] = '/default';
}
}
});
return sanitized;
}
// Usage in token generation
const safeClaims = sanitizeJwtClaims({
userId: user.id,
redirectUrl: req.body.redirectUrl
});
const token = jwt.sign(safeClaims, secret, { expiresIn: '1h' });
Path-based redirects: Use relative paths instead of absolute URLs when possible.
// Use relative paths for internal redirects
function handlePostAuthRedirect(req, res) {
const token = req.cookies.jwt;
const redirectPath = req.query.redirect || '/dashboard';
try {
jwt.verify(token, process.env.JWT_SECRET);
// Only allow relative paths or specific internal paths
if (redirectPath.startsWith('/') && !redirectPath.includes('://')) {
res.redirect(redirectPath);
} else {
res.redirect('/dashboard');
}
} catch (error) {
res.redirect('/login');
}
}
middleBrick continuous monitoring: After implementing fixes, use middleBrick's Pro plan to continuously monitor your JWT endpoints for open redirect vulnerabilities.
# Continuous monitoring with middleBrick
middlebrick monitor https://api.yourservice.com \
--schedule=daily \
--test=open-redirect \
--alert=slack \
--threshold=high
This ensures that any new open redirect vulnerabilities introduced during development are caught before they reach production.