HIGH phishing api keysjwt tokens

Phishing Api Keys with Jwt Tokens

How Phishing API Keys Manifests in JWT Tokens

Phishing API keys in JWT tokens occurs when attackers craft deceptive tokens that mimic legitimate ones to gain unauthorized access to API resources. This attack pattern specifically targets JWT's trust model by exploiting how applications validate and trust token claims.

The most common manifestation involves token substitution attacks where attackers intercept or predict JWT tokens and replace critical claims. For example, an attacker might capture a valid token for a low-privilege user and modify the sub (subject) or role claims to escalate privileges:

// Malicious token modification attempt
const jwt = require('jsonwebtoken');

// Intercepted legitimate token
const legitToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';

// Malicious modification of claims
const tamperedPayload = {
  sub: 'legitimate-user-id',
  role: 'admin', // Escalated privilege
  iat: Math.floor(Date.now() / 1000),
  exp: Math.floor(Date.now() / 1000) + 3600
};

// Attempt to create phishing token
const phishingToken = jwt.sign(tamperedPayload, 'stolen-secret-key');

Another attack vector involves algorithm confusion attacks where attackers exploit weak JWT validation. Many libraries default to accepting none algorithm or allow algorithm switching from asymmetric (RS256) to symmetric (HS256):

# Algorithm confusion attack
import jwt

# Attacker creates token with 'none' algorithm
payload = {'user_id': 'victim', 'role': 'admin'}
tampered_token = jwt.encode(payload, options={'algorithm': 'none'})

# Vulnerable verification that accepts 'none' algorithm
jwt.decode(tampered_token, verify=False)  # Should never do this!

Time-based phishing attacks exploit clock skew or token reuse across different API endpoints. Attackers capture tokens during one session and replay them on different services that share JWT validation logic but have varying security configurations.

JWT Tokens-Specific Detection

Detecting phishing API keys in JWT tokens requires examining both the token structure and validation process. The first line of defense is algorithm validation - ensure your JWT library strictly enforces expected algorithms:

// Secure JWT verification with algorithm validation
const jwt = require('jsonwebtoken');

function verifyToken(token, publicKey) {
  try {
    const decoded = jwt.verify(token, publicKey, {
      algorithms: ['RS256'], // Whitelist only expected algorithms
      issuer: 'your-issuer.com',
      audience: 'your-audience',
      clockTolerance: 30  // 30 seconds clock skew
    });
    return decoded;
  } catch (error) {
    console.error('Token verification failed:', error.message);
    return null;
  }
}

Runtime scanning with middleBrick can automatically detect JWT phishing vulnerabilities by testing for common weaknesses. The scanner checks for algorithm confusion vulnerabilities, weak secret usage, and improper claim validation:

Check Type What It Tests Risk Level
Algorithm Validation Tests if 'none' or unexpected algorithms are accepted Critical
Secret Strength Identifies weak or default signing secrets High
Claim Validation Checks for missing issuer/audience validation High
Clock Skew Handling Tests tolerance for expired tokens Medium

middleBrick's black-box scanning approach tests your JWT endpoints without requiring source code access. It submits crafted tokens to observe how your API responds, identifying vulnerabilities that traditional static analysis might miss.

Additional detection methods include monitoring for unusual token patterns in your logs. Look for tokens with unexpected claim combinations, rapid role changes, or tokens originating from unusual IP addresses or geographic locations.

JWT Tokens-Specific Remediation

Securing JWT tokens against phishing attacks requires implementing defense-in-depth strategies. Start with proper algorithm selection and validation. Always use asymmetric algorithms (RS256, ES256) for production systems where the signing key never needs to be shared with the API server:

// Secure JWT setup with RS256
import jwt from 'jsonwebtoken';
import fs from 'fs';

// Load public key for verification
const publicKey = fs.readFileSync('/path/to/public.pem');

// Generate token with RS256 (signing done offline)
function generateSecureToken(payload: any): string {
  return jwt.sign(payload, fs.readFileSync('/path/to/private.pem'), {
    algorithm: 'RS256',
    expiresIn: '1h',
    issuer: 'your-issuer.com',
    audience: 'your-audience'
  });
}

// Verify with strict validation
function verifySecureToken(token: string): any {
  return jwt.verify(token, publicKey, {
    algorithms: ['RS256'],
    issuer: 'your-issuer.com',
    audience: 'your-audience'
  });
}

Implement claim validation middleware to ensure tokens contain expected values before processing:

// Express middleware for JWT claim validation
const jwt = require('jsonwebtoken');

function jwtValidationMiddleware(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.status(401).json({ error: 'Missing token' });
  }

  const token = authHeader.substring(7);
  
  try {
    const decoded = jwt.verify(token, process.env.PUBLIC_KEY, {
      algorithms: ['RS256'],
      issuer: 'your-issuer.com',
      audience: 'your-audience'
    });

    // Additional custom claim validation
    if (decoded.role !== 'user' && decoded.role !== 'admin') {
      return res.status(403).json({ error: 'Invalid role' });
    }

    req.user = decoded;
    next();
  } catch (err) {
    if (err.name === 'TokenExpiredError') {
      return res.status(401).json({ error: 'Token expired' });
    }
    return res.status(401).json({ error: 'Invalid token' });
  }
}

Use token binding to tie JWTs to specific client characteristics, making phishing tokens less effective across different sessions:

# Token binding example
import jwt
import hashlib

def create_bound_token(user_id, client_ip, user_agent):
    # Create unique binding value
    binding_value = f"{client_ip}|{user_agent}"
    binding_hash = hashlib.sha256(binding_value.encode()).hexdigest()
    
    payload = {
        'user_id': user_id,
        'binding_hash': binding_hash,
        'iat': int(time.time()),
        'exp': int(time.time()) + 3600
    }
    
    return jwt.encode(payload, 'your-secret-key', algorithm='HS256')

def validate_bound_token(token, client_ip, user_agent):
    try:
        payload = jwt.decode(token, 'your-secret-key', algorithms=['HS256'])
        
        # Verify binding
        expected_binding = f"{client_ip}|{user_agent}"
        expected_hash = hashlib.sha256(expected_binding.encode()).hexdigest()
        
        if payload.get('binding_hash') != expected_hash:
            raise ValueError('Token binding mismatch')
            
        return payload
    except jwt.DecodeError:
        raise ValueError('Invalid token format')

Frequently Asked Questions

How can I tell if my JWT implementation is vulnerable to phishing attacks?
Test your implementation by attempting to modify token claims and observing if verification still succeeds. Look for vulnerabilities like accepting the 'none' algorithm, using weak secrets, or lacking proper claim validation. middleBrick can automatically scan your JWT endpoints and identify these weaknesses in 5-15 seconds without requiring credentials or source code access.
Should I use HS256 or RS256 for JWT signing to prevent phishing?
Use RS256 (asymmetric) for production JWTs where the signing key never needs to be shared with API servers. HS256 requires sharing the secret across all services that verify tokens, increasing phishing risk if any service is compromised. RS256 allows you to keep the private key offline while distributing only the public key for verification, making phishing attacks significantly harder.