HIGH dictionary attackbearer tokens

Dictionary Attack with Bearer Tokens

How Dictionary Attack Manifests in Bearer Tokens

Dictionary attacks on Bearer tokens exploit the stateless nature of JWTs and opaque tokens. Attackers leverage the fact that Bearer tokens grant immediate access without additional verification, making them vulnerable to credential stuffing and brute-force attempts.

The most common attack vector involves harvesting valid tokens from public sources, then using automated scripts to test these tokens across multiple endpoints. Since Bearer tokens contain no built-in rate limiting or usage tracking, an attacker can reuse a single token thousands of times without detection.

Consider this vulnerable pattern:

app.get('/api/user/:id', (req, res) => {
  const token = req.headers.authorization?.replace('Bearer ', '');
  if (!token) return res.status(401).json({error: 'Missing token'});
  
  // No token validation - accepts ANY Bearer token
  const userId = req.params.id;
  const user = await db.getUserById(userId);
  res.json(user);
});

This endpoint accepts any Bearer token and returns user data based solely on the URL parameter. An attacker can cycle through a dictionary of stolen tokens:

for (const token of tokenDictionary) {
  const response = await fetch('https://api.example.com/user/123', {
    headers: { 'Authorization': `Bearer ${token}` }
  });
  if (response.status === 200) {
    console.log('Valid token found:', token);
  }
}

Another variant targets token generation endpoints. Attackers probe for weak token generation algorithms or predictable patterns:

// Vulnerable token generation
function generateToken(userId) {
  return Buffer.from(`user:${userId}:secret`).toString('base64');
}

// Attacker dictionary: user:1:secret, user:2:secret, user:3:secret...

Time-based attacks are particularly effective with Bearer tokens. Since tokens are often valid for extended periods (1-24 hours), attackers can validate large token dictionaries without triggering timeouts.

Bearer Tokens-Specific Detection

Detecting dictionary attacks on Bearer tokens requires monitoring both authentication patterns and token usage characteristics. The stateless nature of Bearer tokens means traditional rate limiting at the authentication layer is ineffective.

Key detection indicators include:

  • Multiple token validation failures from the same IP address within short timeframes
  • Sequential token patterns in authorization headers (common in predictable token generation)
  • High-volume requests to token introspection or validation endpoints
  • Unusual token rotation patterns - tokens being used immediately after issuance

Implementation example using middleware:

const tokenAttemptTracker = new Map();

function dictionaryAttackDetector(req, res, next) {
  const ip = req.ip;
  const token = req.headers.authorization?.replace('Bearer ', '');
  
  if (token) {
    const attempts = tokenAttemptTracker.get(ip) || [];
    attempts.push(Date.now());
    
    // Keep last 100 attempts
    if (attempts.length > 100) attempts.shift();
    tokenAttemptTracker.set(ip, attempts);
    
    // Detect rapid sequential attempts
    if (attempts.length >= 10) {
      const timeWindow = attempts[attempts.length - 1] - attempts[0];
      if (timeWindow < 5000) { // 5 seconds
        console.warn('Potential dictionary attack from', ip);
        return res.status(429).json({error: 'Rate limited'});
      }
    }
  }
  
  next();
}

Advanced detection involves analyzing token structure itself. Many vulnerable implementations use predictable token formats:

function analyzeTokenStructure(token) {
  try {
    const parts = token.split('.');
    if (parts.length !== 3) return 'invalid';
    
    // Check for predictable patterns
    const header = JSON.parse(Buffer.from(parts[0], 'base64').toString());
    const payload = JSON.parse(Buffer.from(parts[1], 'base64').toString());
    
    // Detect sequential user IDs
    if (payload.sub && /^user:/.test(payload.sub)) {
      const id = parseInt(payload.sub.split(':')[1]);
      if (id < 1000) return 'predictable';
    }
    
    return 'valid';
  } catch {
    return 'invalid';
  }
}

middleBrick's scanner specifically tests for Bearer token vulnerabilities by attempting to use common token patterns, analyzing token generation endpoints, and checking for missing rate limiting on token validation endpoints.

Bearer Tokens-Specific Remediation

Remediating dictionary attacks on Bearer tokens requires a defense-in-depth approach. The most effective strategy combines token validation improvements with request monitoring and access controls.

First, implement strict token validation with expiration and scope checking:

const jwt = require('jsonwebtoken');

function validateBearerToken(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({error: 'Missing Bearer token'});
  }
  
  const token = authHeader.substring(7);
  
  try {
    const decoded = jwt.verify(token, process.env.JWT_SECRET, {
      algorithms: ['HS256'],
      maxAge: '15m' // Short expiration
    });
    
    // Check token scope and permissions
    if (!decoded.scope.includes('read:user')) {
      return res.status(403).json({error: 'Insufficient permissions'});
    }
    
    req.user = decoded;
    next();
  } catch (err) {
    console.warn('Token validation failed:', err.message);
    return res.status(401).json({error: 'Invalid token'});
  }
}

Add request-level rate limiting that considers token context:

const rateLimit = require('express-rate-limit');

const limiter = rateLimit({
  keyGenerator: (req) => {
    const token = req.headers.authorization?.replace('Bearer ', '');
    return token || req.ip; // Rate limit by token when available
  },
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 100, // Limit each token to 100 requests
  message: 'Too many requests from this token'
});

app.use('/api/', limiter);

Implement token binding to specific client characteristics:

function bindTokenToClient(token, req) {
  const decoded = jwt.decode(token, {complete: true});
  
  // Add client fingerprint to token payload
  const fingerprint = crypto.createHash('sha256')
    .update(req.headers['user-agent'] + req.ip)
    .digest('hex');
  
  decoded.payload.fingerprint = fingerprint;
  
  return jwt.sign(decoded.payload, process.env.JWT_SECRET, {
    algorithm: 'HS256',
    expiresIn: '15m'
  });
}

// Validate fingerprint on each request
function validateTokenFingerprint(req, token) {
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  const currentFingerprint = crypto.createHash('sha256')
    .update(req.headers['user-agent'] + req.ip)
    .digest('hex');
  
  if (decoded.fingerprint !== currentFingerprint) {
    throw new Error('Token client mismatch');
  }
}

For high-security scenarios, implement token rotation with refresh tokens:

app.post('/refresh', (req, res) => {
  const refreshToken = req.cookies.refreshToken;
  
  try {
    const decoded = jwt.verify(refreshToken, process.env.REFRESH_SECRET);
    
    // Check if refresh token is still valid (not revoked)
    const isValid = await db.checkRefreshToken(decoded.jti);
    if (!isValid) throw new Error('Revoked token');
    
    // Issue new access token
    const newToken = jwt.sign({
      userId: decoded.userId,
      scope: decoded.scope
    }, process.env.JWT_SECRET, {expiresIn: '15m'});
    
    res.json({token: newToken});
  } catch (err) {
    res.status(401).json({error: 'Invalid refresh token'});
  }
});

Frequently Asked Questions

How can I tell if my Bearer token implementation is vulnerable to dictionary attacks?

Look for these indicators: tokens that never expire, endpoints that accept any Bearer token without validation, lack of rate limiting on token validation endpoints, predictable token generation patterns, and missing client binding. Run middleBrick's scanner to automatically detect these vulnerabilities - it tests your API with common attack patterns and provides specific remediation guidance.

What's the difference between a dictionary attack and credential stuffing?

Dictionary attacks use a predefined list of likely values (passwords, tokens, patterns) systematically tested against an authentication system. Credential stuffing specifically uses breached username/password combinations from other sites. For Bearer tokens, dictionary attacks typically involve testing stolen or guessed tokens, while credential stuffing would involve testing username/password pairs to obtain tokens in the first place. Both exploit reused credentials, but dictionary attacks focus on the token validation phase rather than initial authentication.