HIGH bola idorbasic auth

Bola Idor with Basic Auth

How Bola Idor Manifests in Basic Auth

Basic Authentication's simplicity creates a perfect storm for BOLA/IdOR vulnerabilities. The stateless nature of Basic Auth means credentials are validated once per request, then the server trusts that the authenticated user should have access to whatever resource they request. This trust becomes dangerous when authorization checks are missing or improperly implemented.

Consider a REST API endpoint that retrieves user data:

app.get('/api/users/:id', authenticateBasic, (req, res) => {
  const userId = req.params.id;
  const user = await db.getUserById(userId);
  res.json(user);
});

The vulnerability here is subtle but critical: the endpoint authenticates the request using Basic Auth credentials, but never verifies whether the authenticated user actually owns or should access the requested user ID. An attacker can simply change the ID parameter in the URL to access any user's data.

Another common pattern involves collection endpoints that return filtered results based on query parameters:

app.get('/api/users', authenticateBasic, (req, res) => {
  const userId = req.query.userId;
  const users = await db.getUsers({ ownerId: userId });
  res.json(users);
});

Here, an attacker can manipulate the userId query parameter to access data belonging to other users, even though they're properly authenticated with Basic Auth.

Resource ownership becomes particularly problematic with Basic Auth because the protocol itself provides no context about user relationships or permissions. The server only knows the username and password are valid, not what the user should be allowed to access.

Multi-tenant applications are especially vulnerable. Consider a SaaS platform where each tenant has their own data:

app.get('/api/tenants/:tenantId/data', authenticateBasic, (req, res) => {
  const tenantId = req.params.tenantId;
  const data = await db.getTenantData(tenantId);
  res.json(data);
});

Without proper authorization checks, any authenticated user can access any tenant's data by simply changing the tenantId parameter.

The problem compounds when Basic Auth is used with client-side applications that can easily modify request parameters. Since Basic Auth credentials are sent with every request, there's no additional server-side session context to validate against.

Database queries that use unvalidated parameters are particularly dangerous:

app.get('/api/orders/:orderId', authenticateBasic, async (req, res) => {
  const orderId = req.params.orderId;
  const order = await db.query(`
    SELECT * FROM orders WHERE id = ${orderId}
  `);
  res.json(order);
});

This code authenticates the user but doesn't verify they own the order they're trying to access. An attacker can enumerate order IDs to access any customer's orders.

Even seemingly secure patterns can fail when authorization is assumed rather than explicitly checked. For example:

app.get('/api/profile', authenticateBasic, (req, res) => {
  const username = req.user.username;
  const profile = await db.getUserProfile(username);
  res.json(profile);
});

While this appears safer, it still fails if the application allows users to view other profiles through different endpoints or if the username parameter can be manipulated in related API calls.

The fundamental issue is that Basic Auth provides authentication without authorization. The protocol tells you who the user is, but nothing about what they should be allowed to do. This separation of concerns is precisely where BOLA/IdOR vulnerabilities emerge.

Basic Auth-Specific Detection

Detecting BOLA/IdOR vulnerabilities in Basic Auth implementations requires both automated scanning and manual code review. The stateless nature of Basic Auth means traditional session-based detection methods don't apply, requiring specialized approaches.

Automated scanning with middleBrick specifically targets Basic Auth endpoints by testing parameter manipulation across authenticated requests. The scanner systematically modifies resource identifiers in URLs, query parameters, and request bodies to see if unauthorized data access is possible.

For example, when scanning an endpoint like /api/users/123, middleBrick will:

  • Test sequential ID values (122, 124, 125) to identify pattern-based access
  • Try common ID formats (UUIDs, emails, usernames) if the original ID is one format
  • Modify query parameters that might control data filtering or ownership
  • Check for inconsistent authorization across similar endpoints
  • Test for IDOR in related resources (user orders, settings, preferences)
  • Validate that authentication headers are properly scoped to the requested resource

The scanner also examines OpenAPI specifications to identify endpoints that accept user-identifying parameters without proper authorization checks. This spec analysis catches vulnerabilities that might not be triggered during active scanning due to business logic constraints.

Manual detection requires examining code paths where Basic Auth credentials are validated but resource access isn't properly scoped. Key indicators include:

function authenticateBasic(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader) return res.status(401).send('Missing auth');
  
  const [scheme, credentials] = authHeader.split(' ');
  if (scheme.toLowerCase() !== 'basic') return res.status(401).send('Invalid scheme');
  
  const decoded = Buffer.from(credentials, 'base64').toString();
  const [username, password] = decoded.split(':');
  
  const user = db.getUserByUsername(username);
  if (!user || user.password !== password) return res.status(401).send('Invalid credentials');
  
  req.user = user;
  next();
}

The critical vulnerability in this middleware is that it only validates credentials but doesn't establish any authorization context for subsequent requests.

Testing should also examine how Basic Auth is implemented across different environments. Development, staging, and production often have different user datasets, potentially masking IDOR vulnerabilities that only appear with specific data combinations.

Parameter pollution attacks are particularly relevant for Basic Auth endpoints. Testers should try:

/api/users?id=123&id=456  // Parameter pollution
/api/users/123?format=json&format=xml  // Multiple parameters
/api/users/123?debug=true&admin=true  // Privilege escalation attempts

Rate limiting and authentication retry mechanisms can also reveal authorization weaknesses. If an endpoint allows unlimited authentication attempts but restricts access based on user roles, attackers might use credential stuffing to bypass authorization.

Cross-tenant data access testing is essential for multi-tenant applications using Basic Auth. Verify that tenant boundaries are properly enforced by attempting to access data across different tenant contexts with the same credentials.

Finally, examine how Basic Auth interacts with other security controls. WAF rules, API gateways, and CDN configurations might provide additional authorization layers that aren't visible in the application code but are critical for preventing BOLA/IdOR attacks.

Basic Auth-Specific Remediation

Remediating BOLA/IdOR vulnerabilities in Basic Auth implementations requires a systematic approach to authorization that goes beyond simple authentication. The solution involves implementing proper access controls and validating resource ownership at every access point.

The foundation of remediation is establishing a clear authorization model. For Basic Auth, this typically means:

  1. Mapping authenticated users to their authorized resources
  2. Implementing ownership verification for all data access
  3. Using parameterized queries with proper scoping
  4. Adding authorization middleware that validates access rights

Here's a secure implementation pattern for Basic Auth endpoints:

function authorizeResourceAccess(req, res, next) {
  const resourceId = req.params.id;
  const userId = req.user.id;
  
  // Verify the authenticated user owns this resource
  const isOwner = db.verifyResourceOwnership(userId, resourceId);
  
  if (!isOwner) {
    return res.status(403).json({
      error: 'Access denied',
      message: 'You do not have permission to access this resource'
    });
  }
  
  next();
}

This middleware should be added to any endpoint that accesses user-specific resources:

app.get('/api/users/:id', authenticateBasic, authorizeResourceAccess, async (req, res) => {
  const userId = req.params.id;
  const user = await db.getUserById(userId);
  res.json(user);
});

For collection endpoints that return filtered results, implement proper scoping:

app.get('/api/users', authenticateBasic, async (req, res) => {
  const userId = req.user.id;
  const users = await db.getUsers({ ownerId: userId });
  res.json(users);
});

Database queries should use parameterized statements with proper ownership validation:

app.get('/api/orders/:orderId', authenticateBasic, async (req, res) => {
  const orderId = req.params.orderId;
  const userId = req.user.id;
  
  const order = await db.query(
    'SELECT * FROM orders WHERE id = $1 AND user_id = $2',
    [orderId, userId]
  );
  
  if (!order) {
    return res.status(404).json({ error: 'Order not found or access denied' });
  }
  
  res.json(order);
});

For multi-tenant applications, implement tenant-scoped authorization:

app.get('/api/tenants/:tenantId/data', authenticateBasic, async (req, res) => {
  const tenantId = req.params.tenantId;
  const userId = req.user.id;
  
  // Verify user belongs to this tenant
  const userTenant = await db.getUserTenant(userId);
  if (userTenant.id !== tenantId) {
    return res.status(403).json({ error: 'Access denied' });
  }
  
  const data = await db.getTenantData(tenantId);
  res.json(data);
});

Implement role-based access control (RBAC) for more granular permissions:

const roles = {
  admin: ['users', 'orders', 'settings'],
  user: ['own-orders', 'profile'],
  viewer: ['public-data']
};

function checkAuthorization(requiredPermission) {
  return function(req, res, next) {
    const userPermissions = roles[req.user.role] || [];
    if (!userPermissions.includes(requiredPermission)) {
      return res.status(403).json({ error: 'Insufficient permissions' });
    }
    next();
  };
}

Apply this to endpoints:

app.get('/api/admin/users', authenticateBasic, checkAuthorization('users'), async (req, res) => {
  const users = await db.getAllUsers();
  res.json(users);
});

For APIs that accept complex filtering parameters, validate that filters don't allow cross-user access:

app.get('/api/search', authenticateBasic, async (req, res) => {
  const userId = req.user.id;
  const { search, category } = req.query;
  
  // Ensure search results are scoped to the authenticated user
  const results = await db.search(
    { search, category, userId },
    { limit: 50 }
  );
  
  res.json(results);
});

Implement comprehensive logging for authorization failures to detect potential attacks:

function logAuthorizationFailure(userId, resourceId, action) {
  console.log(`Authorization failed: user ${userId} attempted ${action} on resource ${resourceId}`);
  // Send to monitoring system
}

Finally, implement automated testing for authorization controls:

describe('Authorization tests', () => {
  test('User cannot access other users data', async () => {
    const user1 = await createUser();
    const user2 = await createUser();
    
    // User1 creates data
    const data = await api.post('/api/data', {
      auth: basicAuth(user1.username, user1.password),
      body: { content: 'test' }
    });
    
    // User2 tries to access it
    const response = await api.get(`/api/data/${data.id}`, {
      auth: basicAuth(user2.username, user2.password)
    });
    
    expect(response.status).toBe(403);
  });
});

These remediation patterns ensure that Basic Auth authentication is properly coupled with authorization checks, preventing BOLA/IdOR vulnerabilities while maintaining the protocol's simplicity and statelessness.

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 does Basic Auth's statelessness make BOLA/IdOR vulnerabilities harder to detect?
Basic Auth sends credentials with every request and maintains no session state on the server. This means traditional session-based authorization checks don't apply. Each request must independently verify that the authenticated user has permission to access the specific resource being requested. Without proper authorization middleware, the server only knows the credentials are valid, not whether the user should access that particular resource. This creates a window where authenticated users can manipulate request parameters to access unauthorized data.
Can Basic Auth be used securely with APIs that need fine-grained access control?
Yes, but it requires implementing robust authorization layers on top of Basic Auth authentication. The key is to never assume that valid credentials grant access to all resources. Every endpoint that accesses user-specific data should validate resource ownership, typically by checking that the authenticated user ID matches the resource owner ID in database queries. For more complex scenarios, implement role-based access control (RBAC) or attribute-based access control (ABAC) that defines what each authenticated user can access. The Basic Auth credentials authenticate who the user is, while your authorization system determines what they can do.