HIGH insecure direct object referencejwt tokens

Insecure Direct Object Reference with Jwt Tokens

How Insecure Direct Object Reference Manifests in Jwt Tokens

Insecure Direct Object Reference (IDOR) in JWT tokens occurs when attackers manipulate token claims to access unauthorized resources. The stateless nature of JWTs makes them particularly vulnerable to IDOR attacks when developers improperly trust client-side claims without proper validation.

A common JWT IDOR pattern involves the sub (subject) claim. Consider an API endpoint that retrieves user data:

const jwt = require('jsonwebtoken');

// Vulnerable implementation
app.get('/api/user/:id', (req, res) => {
  const token = req.headers.authorization.split(' ')[1];
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  
  // TRUSTING CLIENT-SIDE CLAIM WITHOUT VALIDATION
  const userId = decoded.sub;
  
  // Attacker can modify 'sub' claim to access any user
  const userData = db.query('SELECT * FROM users WHERE id = ?', [userId]);
  res.json(userData);
});

An attacker can easily modify the sub claim using tools like jwt.io or by crafting tokens offline. If they know another user's ID, they can access that user's data simply by changing the sub value in the token.

Another JWT-specific IDOR vulnerability involves role-based escalation through custom claims:

// Vulnerable: trusting client-side role claims
app.get('/api/admin/dashboard', (req, res) => {
  const token = req.headers.authorization.split(' ')[1];
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  
  // Attacker modifies 'role' claim to 'admin'
  if (decoded.role === 'admin') {
    res.json(adminDashboardData);
  } else {
    res.status(403).send('Forbidden');
  }
});

Database IDOR with JWTs is particularly dangerous because the token persists across requests. An attacker can harvest valid tokens from one endpoint and use them to access other endpoints with different authorization requirements:

// Vulnerable: using token data for database queries without ownership validation
app.get('/api/documents/:docId', (req, res) => {
  const token = req.headers.authorization.split(' ')[1];
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  
  // No validation that user owns this document
  const doc = db.query('SELECT * FROM documents WHERE id = ?', [req.params.docId]);
  res.json(doc);
});

Multi-tenant applications face severe JWT IDOR risks when tenant IDs are embedded in tokens but not validated against requested resources:

// Vulnerable multi-tenant implementation
app.get('/api/tenant/:tenantId/resources', (req, res) => {
  const token = req.headers.authorization.split(' ')[1];
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  
  // Attacker modifies 'tenantId' claim to access other tenants
  const tenantId = decoded.tenantId;
  const resources = db.query('SELECT * FROM resources WHERE tenant_id = ?', [tenantId]);
  res.json(resources);
});

Even with proper JWT verification, IDOR persists if the application logic trusts claims without server-side ownership validation. The token verification only ensures the token is authentic, not that the claims represent authorized access.

JWT-Specific Detection

Detecting JWT IDOR requires both static analysis of token handling patterns and dynamic testing of authorization logic. middleBrick's JWT-specific scanning identifies these vulnerabilities through several techniques.

middleBrick's JWT scanner examines token parsing and claim usage patterns. It looks for common anti-patterns like trusting sub, role, or custom claims without server-side validation. The scanner tests endpoints by crafting JWTs with modified claims and observing responses:

// middleBrick test payload for JWT IDOR detection
{
  "alg": "HS256",
  "typ": "JWT",
  "sub": "999999",           // Modified subject claim
  "role": "admin",           // Escalated role claim
  "tenantId": "666666"       // Modified tenant claim
}

The scanner verifies whether endpoints return data for modified claims, indicating IDOR vulnerabilities. It also tests for predictable JWT structure issues, such as using weak secrets or vulnerable algorithms like none.

middleBrick's dynamic analysis includes testing JWT endpoints with crafted tokens that have:

  • Modified sub claims to access other users' data
  • Escalated role or permission claims
  • Invalid or expired tokens to test error handling
  • Tokens with none algorithm to test algorithm confusion

The scanner also examines API specifications for JWT usage patterns. When analyzing OpenAPI specs, middleBrick cross-references token requirements with endpoint authorization logic to identify mismatches between documented security requirements and actual implementation.

middleBrick's LLM/AI security module specifically detects prompt injection vulnerabilities in AI-powered JWT processing systems, where attackers might manipulate token claims to influence AI model behavior or extract sensitive information from AI responses.

JWT-Specific Remediation

Fixing JWT IDOR requires a defense-in-depth approach that validates both token authenticity and resource ownership. The most critical principle: never trust client-side claims for authorization decisions.

Proper JWT IDOR remediation involves server-side validation of resource ownership:

// Secure implementation with ownership validation
app.get('/api/documents/:docId', (req, res) => {
  const token = req.headers.authorization.split(' ')[1];
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  
  // VALIDATE OWNERSHIP ON SERVER SIDE
  const doc = db.query(
    'SELECT * FROM documents WHERE id = ? AND user_id = ?', 
    [req.params.docId, decoded.sub]
  );
  
  if (!doc) {
    return res.status(404).send('Document not found or unauthorized');
  }
  
  res.json(doc);
});

For role-based access control with JWTs, implement server-side role validation using database lookups rather than trusting token claims:

// Secure role-based access control
app.get('/api/admin/dashboard', (req, res) => {
  const token = req.headers.authorization.split(' ')[1];
  const decoded = jwt.verify(token, process.env.JWT_SECRET);
  
  // VERIFY ROLE IN DATABASE, NOT FROM TOKEN CLAIM
  const user = db.query('SELECT role FROM users WHERE id = ?', [decoded.sub]);
  
  if (user.role !== 'admin') {
    return res.status(403).send('Forbidden');
  }
  
  res.json(adminDashboardData);
});

Implement proper JWT secret management and algorithm validation:

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

function verifyToken(token) {
  try {
    // Verify with secret and algorithm
    const decoded = jwt.verify(token, process.env.JWT_SECRET, {
      algorithms: ['HS256']
    });
    return decoded;
  } catch (err) {
    // Handle verification errors securely
    if (err.name === 'JsonWebTokenError') {
      throw new Error('Invalid token format');
    }
    if (err.name === 'TokenExpiredError') {
      throw new Error('Token expired');
    }
    throw new Error('Token verification failed');
  }
}

Use middleware for consistent authorization checks across your API:

// Authorization middleware
function authorizeResource(requiredRole) {
  return (req, res, next) => {
    const token = req.headers.authorization?.split(' ')[1];
    if (!token) return res.status(401).send('Missing token');
    
    try {
      const decoded = jwt.verify(token, process.env.JWT_SECRET);
      
      // Check user role in database
      const user = db.query('SELECT role FROM users WHERE id = ?', [decoded.sub]);
      
      if (!user || user.role !== requiredRole) {
        return res.status(403).send('Insufficient permissions');
      }
      
      req.user = user;
      next();
    } catch (err) {
      res.status(401).send('Invalid token');
    }
  };
}

// Usage
app.get('/api/admin/dashboard', authorizeResource('admin'), (req, res) => {
  res.json(adminDashboardData);
});

Implement token revocation and rotation strategies to limit the impact of compromised tokens. Use short expiration times (5-15 minutes for access tokens) and refresh tokens for extended sessions.

Related CWEs: bolaAuthorization

CWE IDNameSeverity
CWE-250Execution with Unnecessary Privileges HIGH
CWE-639Insecure Direct Object Reference CRITICAL
CWE-732Incorrect Permission Assignment HIGH

Frequently Asked Questions

How can I test my JWT endpoints for IDOR vulnerabilities?
Use middleBrick's free scanner to test your JWT endpoints. It automatically crafts modified JWTs with altered claims and tests whether your endpoints return unauthorized data. The scanner also checks for weak JWT implementations like the 'none' algorithm and predictable secrets.
What's the difference between JWT verification and authorization?
JWT verification confirms the token is authentic and hasn't been tampered with using your secret key. Authorization determines whether the authenticated user has permission to access specific resources. Verification alone doesn't prevent IDOR—you must also validate that the user owns or has explicit permission to access requested resources.