HIGH CWE-639 BOLA/IDOR

CWE-639 in APIs

What is CWE-639?

CWE-639, Authorization Bypass Through User-Controlled Key, is a critical weakness where an application uses a key or index (such as a user ID, database key, or array index) that can be manipulated by an attacker to access unauthorized data or functionality. The core issue is that the application trusts user-supplied values to determine what data to access or what operations to perform, without properly validating whether the user has permission to access that specific resource.

The weakness occurs when developers use identifiers like database primary keys, array indices, or resource IDs directly from user input without verifying the user's authorization for that specific resource. This creates a direct path for attackers to enumerate or guess valid identifiers and access data belonging to other users.

CWE-639 in API Contexts

In API environments, CWE-639 manifests most commonly as Insecure Direct Object References (IDOR) and Broken Object Level Authorization (BOLA). These vulnerabilities appear when APIs expose endpoints that accept resource identifiers (IDs, UUIDs, keys) in parameters or paths, then use those identifiers to fetch data without verifying the caller's permissions.

// VULNERABLE: No authorization check
app.get('/api/users/:userId', (req, res) => {
  const userId = req.params.userId;
  const user = db.query('SELECT * FROM users WHERE id = ?', [userId]);
  res.json(user);
});

The API above trusts the userId parameter and returns whatever user record matches that ID. An attacker can simply change the userId value to access any user's data. This pattern is especially dangerous in REST APIs where resource IDs are sequential or predictable.

Common API scenarios include:

  • Viewing another user's profile, documents, or settings by changing the ID in the URL
  • Accessing order history, payment information, or account details by manipulating order IDs
  • Modifying resources (PUT/PATCH/DELETE) without verifying ownership
  • Batch operations that process multiple resources without per-resource authorization

REST APIs are particularly vulnerable because they expose resource identifiers directly in URLs, making it trivial for attackers to enumerate and test different values. GraphQL APIs face similar risks when resolvers don't validate user permissions for requested objects.

Detection

Detecting CWE-639 requires both static code analysis and dynamic testing. Static analysis can identify patterns where user input is used directly as database keys or resource identifiers without authorization checks. Dynamic testing involves systematically testing API endpoints with modified identifiers to see if unauthorized access is possible.

Manual testing techniques include:

  • Parameter fuzzing: systematically changing numeric IDs, UUIDs, and other identifiers in API requests
  • Authentication bypass testing: testing endpoints with different user credentials while keeping resource identifiers constant
  • Authorization matrix testing: verifying that users can only access resources they own or have been explicitly granted access to
  • Batch operation testing: testing endpoints that accept arrays of IDs to ensure each item is properly authorized

For automated detection, middleBrick's BOLA/IDOR scanner specifically targets this weakness. The scanner tests API endpoints by:

  • Identifying endpoints that accept resource identifiers in parameters or paths
  • Testing with identifiers from different user contexts to verify proper authorization
  • Checking for predictable identifier patterns that could be enumerated
  • Analyzing response differences that might indicate successful unauthorized access

middleBrick's approach is particularly effective because it operates without credentials, testing the unauthenticated attack surface that many developers overlook. The scanner can identify endpoints vulnerable to CWE-639 even when you don't have test accounts for every user role.

Here's an example of how middleBrick might detect this vulnerability:

{
  "finding": "Authorization Bypass Through User-Controlled Key",
  "severity": "High",
  "category": "BOLA/IDOR",
  "endpoint": "/api/users/123",
  "test_case": "Changed user ID from 123 to 124 and received different user data",
  "remediation": "Implement proper authorization checks to verify resource ownership before access"
}

The scanner's parallel testing approach can quickly identify multiple instances of this weakness across your API surface, providing prioritized findings with severity levels to help you focus on the most critical vulnerabilities first.

Remediation

Fixing CWE-639 requires implementing proper authorization checks at the resource level. The fundamental principle is: never trust user-supplied identifiers, always verify the user's right to access the specific resource they're requesting.

The most robust approach is to implement a permissions layer that validates access rights before any data access occurs. Here's a secure implementation pattern:

// SECURE: Proper authorization check
app.get('/api/users/:userId', (req, res) => {
  const requestedUserId = req.params.userId;
  const authenticatedUserId = req.user.id; // from authentication middleware
  
  // Verify the user is accessing their own resource
  if (requestedUserId !== authenticatedUserId) {
    return res.status(403).json({
      error: 'Forbidden: You can only access your own profile'
    });
  }
  
  const user = db.query('SELECT * FROM users WHERE id = ?', [requestedUserId]);
  res.json(user);
});

For more complex scenarios where users might have access to resources owned by others (team environments, shared resources), implement a permissions check function:

function checkResourceAccess(userId, resourceId, resourceType) {
  // Check if user owns the resource
  const ownership = db.query(
    'SELECT 1 FROM resources WHERE id = ? AND owner_id = ?',
    [resourceId, userId]
  );
  
  if (ownership) return true;
  
  // Check if user has explicit permissions
  const permissions = db.query(
    'SELECT 1 FROM resource_permissions WHERE 
     resource_id = ? AND user_id = ? AND resource_type = ?',
    [resourceId, userId, resourceType]
  );
  
  return permissions !== null;
}

app.get('/api/documents/:docId', (req, res) => {
  const docId = req.params.docId;
  const userId = req.user.id;
  
  if (!checkResourceAccess(userId, docId, 'document')) {
    return res.status(403).json({ error: 'Access denied' });
  }
  
  const document = db.query('SELECT * FROM documents WHERE id = ?', [docId]);
  res.json(document);
});

Additional remediation strategies:

  • Reference mapping: Instead of using raw database IDs, map user-specific identifiers to actual resource IDs on the server side
  • Indirect reference maps: Use per-session or per-user mappings that translate external identifiers to internal ones
  • Least privilege principle: Ensure users only receive tokens or sessions with the minimum permissions needed
  • Audit logging: Log all access attempts to sensitive resources, successful or not

For APIs with complex authorization requirements, consider implementing an authorization framework like Casbin or using policy-based access control (PBAC) to manage resource-level permissions centrally.

Remember that fixing CWE-639 is not just about adding authorization checks—it's about designing your API to never expose direct object references in the first place. Consider using opaque identifiers, implementing proper access control lists, and regularly testing your authorization logic with tools like middleBrick to ensure vulnerabilities don't creep back in.

Frequently Asked Questions

How is CWE-639 different from broken authentication?
CWE-639 is about authorization (what you can do once authenticated), while broken authentication is about verifying who you are. You can have perfect authentication but still suffer from CWE-639 if your API allows authenticated users to access any resource by guessing IDs. Think of authentication as checking your ID at the door, and authorization as checking if you're allowed in the specific room you're trying to enter.
Can middleBrick detect all instances of CWE-639?
middleBrick's BOLA/IDOR scanner is highly effective at detecting many instances of CWE-639 by testing endpoints with modified identifiers and checking for unauthorized access. However, no automated tool can catch 100% of cases, especially those requiring complex business logic understanding. middleBrick provides excellent coverage for common patterns and should be part of a comprehensive security testing strategy that includes manual code review and penetration testing.