HIGH insecure direct object referencebasic auth

Insecure Direct Object Reference with Basic Auth

How Insecure Direct Object Reference Manifests in Basic Auth

Insecure Direct Object Reference (IDOR) in Basic Auth contexts occurs when an API relies solely on HTTP Basic Authentication for access control but fails to properly validate whether the authenticated user has permission to access specific resources. This creates a critical vulnerability where attackers can enumerate and access other users' data simply by modifying resource identifiers.

The fundamental problem arises because Basic Auth provides authentication (who you are) but doesn't inherently provide authorization (what you can access). Many developers mistakenly assume that successful Basic Auth authentication grants access to all resources, leading to dangerous authorization bypasses.

Consider a banking API where users authenticate with Basic Auth to access their account information. A typical vulnerable endpoint might look like this:

GET /api/accounts/12345
Authorization: Basic Ym9iOnBhc3N3b3Jk

The server authenticates Bob successfully but then retrieves account 12345 without verifying that Bob owns this account. An attacker can simply increment the account ID:

GET /api/accounts/12346
Authorization: Basic Ym9iOnBhc3N3b3Jk

If the server doesn't validate ownership, the attacker now accesses Alice's account data. This pattern appears frequently in REST APIs where resource IDs are sequential integers or predictable UUIDs.

Basic Auth's stateless nature makes this particularly dangerous. Since credentials are sent with every request, attackers can easily script enumeration attempts. A simple bash script can test thousands of account IDs:

for i in {1000..2000}; do
  curl -s -o /dev/null -w "%{http_code}" \
    -H "Authorization: Basic $(echo -n 'bob:password' | base64)" \
    "https://api.example.com/accounts/$i"
done

Another common pattern involves document or file access endpoints:

GET /api/documents/abc123.pdf
Authorization: Basic Ym9iOnBhc3N3b3Jk

Without proper authorization checks, attackers can access any document by guessing or brute-forcing document IDs. This becomes especially problematic when document IDs follow predictable patterns like UUIDs without version 4 randomness.

Multi-tenant applications using Basic Auth face similar risks. Consider a SaaS platform where tenants authenticate but can access other tenants' data:

GET /api/tenants/456/projects/789
Authorization: Basic Ym9iOnBhc3N3b3Jk

The API authenticates the user but fails to verify that the tenant making the request owns tenant 456. This allows cross-tenant data exposure across the entire platform.

Database query patterns often reveal these vulnerabilities. A vulnerable implementation might directly use request parameters without ownership validation:

const userId = getAuthenticatedUserId(req);
const accountId = req.params.accountId;
const account = await db.query(
  'SELECT * FROM accounts WHERE id = $1', [accountId]
);

The fix requires adding authorization checks:

const userId = getAuthenticatedUserId(req);
const accountId = req.params.accountId;
const account = await db.query(
  'SELECT * FROM accounts WHERE id = $1 AND user_id = $2', 
  [accountId, userId]
);
if (!account) {
  return res.status(404).json({ error: 'Account not found' });
}

Basic Auth's simplicity can lull developers into a false sense of security. Since it's built into HTTP and requires minimal implementation, teams often skip proper authorization layer development, assuming authentication alone provides sufficient protection.

Basic Auth-Specific Detection

Detecting IDOR vulnerabilities in Basic Auth APIs requires both automated scanning and manual testing approaches. The stateless nature of Basic Auth makes it particularly amenable to automated enumeration attacks.

Automated scanning with middleBrick provides comprehensive IDOR detection for Basic Auth endpoints. The scanner tests authenticated endpoints by automatically extracting Basic Auth credentials from captured requests and then systematically testing for authorization bypasses. For each authenticated endpoint discovered, middleBrick attempts to access resources using modified identifiers while maintaining the same authentication context.

middleBrick's detection methodology includes sequential ID testing for numeric identifiers, UUID pattern analysis for version 1 and 4 UUIDs, and timestamp-based ID detection. The scanner maintains the authenticated session context while modifying resource identifiers to test for authorization failures.

Manual testing techniques for Basic Auth IDOR include parameter fuzzing and authorization bypass attempts. Start by identifying authenticated endpoints that accept resource identifiers. Common patterns include:

GET /api/users/{userId}/profile
GET /api/orders/{orderId}/details
GET /api/documents/{docId}/download

For each endpoint, attempt to access resources owned by other users by modifying the identifier parameter. If the API returns data without proper authorization errors, you've identified an IDOR vulnerability.

Credential reuse testing is particularly effective with Basic Auth. Since credentials are stateless and sent with every request, you can test whether the API properly isolates user data by using the same credentials to access different resource IDs. A simple curl-based test script:

#!/bin/bash
AUTH="$(echo -n 'testuser:password' | base64)"
BASE_URL="https://api.example.com"

# Test user profile access
for id in {100..110}; do
  echo "Testing user $id..."
  curl -s -H "Authorization: Basic $AUTH" \
    "$BASE_URL/api/users/$id/profile" \
    -w "HTTP %{http_code}\n" \
    -o response.json
  cat response.json
  echo "---"
done

API specification analysis can reveal potential IDOR vulnerabilities before runtime testing. Look for endpoints that accept user-controlled identifiers without proper ownership validation. OpenAPI specs often expose these patterns:

paths:
  /api/users/{userId}/orders:
    get:
      parameters:
        - name: userId
          in: path
          required: true
          schema:
            type: string
      responses:
        '200':
          description: User orders

Without explicit authorization requirements in the spec, this endpoint is likely vulnerable to IDOR attacks.

middleBrick's continuous monitoring capabilities help detect IDOR vulnerabilities that appear over time as APIs evolve. The scanner tracks authenticated endpoints and their authorization behavior, alerting when new endpoints lack proper authorization checks or when existing endpoints' authorization logic changes.

Response analysis provides another detection vector. Compare responses for valid vs invalid resource IDs. IDOR vulnerabilities often return different HTTP status codes or response structures that can be detected programmatically. For example, a secure endpoint might return 404 for unauthorized access, while a vulnerable one returns 200 with another user's data.

Rate limiting bypass testing can reveal IDOR vulnerabilities in Basic Auth contexts. Since Basic Auth credentials are static, attackers can enumerate resources without triggering authentication-based rate limits. Test whether the API implements resource-level rate limiting to prevent enumeration attacks.

Basic Auth-Specific Remediation

Remediating IDOR vulnerabilities in Basic Auth APIs requires implementing proper authorization checks that verify resource ownership for every request. The solution involves adding authorization logic that validates whether the authenticated user has permission to access the requested resource.

The most effective approach is implementing ownership-based authorization checks. For every endpoint that accepts resource identifiers, verify that the authenticated user owns or has explicit permission to access the resource. Here's a Node.js/Express example:

const express = require('express');
const app = express();

// Middleware to extract authenticated user ID from Basic Auth
function authenticateBasicAuth(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Basic ')) {
    return res.status(401).json({ error: 'Missing Basic Auth' });
  }
  
  const base64Credentials = authHeader.split(' ')[1];
  const credentials = Buffer.from(base64Credentials, 'base64').toString('ascii');
  const [username, password] = credentials.split(':');
  
  const userId = validateUserCredentials(username, password);
  if (!userId) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }
  
  req.userId = userId;
  next();
}

// Authorization middleware for resource ownership
function authorizeResourceOwnership(resourceType) {
  return async (req, res, next) => {
    const userId = req.userId;
    const resourceId = req.params.resourceId;
    
    let hasAccess = false;
    switch (resourceType) {
      case 'account':
        hasAccess = await checkAccountOwnership(userId, resourceId);
        break;
      case 'document':
        hasAccess = await checkDocumentAccess(userId, resourceId);
        break;
      case 'order':
        hasAccess = await checkOrderOwnership(userId, resourceId);
        break;
    }
    
    if (!hasAccess) {
      return res.status(404).json({ error: 'Resource not found' });
    }
    next();
  };
}

// Protected endpoint with authorization
app.get('/api/accounts/:accountId', authenticateBasicAuth, 
  authorizeResourceOwnership('account'), (req, res) => {
    const accountId = req.params.accountId;
    const accountData = await getAccountData(accountId);
    res.json(accountData);
});

For database-driven applications, implement authorization at the query level using JOINs or subqueries:

// Secure query with ownership validation
async function getAccountData(userId, accountId) {
  const query = `
    SELECT a.* FROM accounts a
    INNER JOIN users u ON a.user_id = u.id
    WHERE a.id = $1 AND u.id = $2
  `;
  const result = await db.query(query, [accountId, userId]);
  return result.rows[0];
}

Implement role-based access control (RBAC) for more complex authorization scenarios. Define user roles and permissions, then check permissions for each request:

const permissions = {
  'user': ['read:self', 'write:self'],
  'admin': ['read:all', 'write:all', 'read:users', 'write:users']
};

function checkPermission(userRole, requiredPermission) {
  return permissions[userRole]?.includes(requiredPermission);
}

// Usage in endpoint
app.get('/api/users/:userId/profile', authenticateBasicAuth, (req, res) => {
  const userId = req.params.userId;
  const authenticatedUserId = req.userId;
  const userRole = await getUserRole(authenticatedUserId);
  
  // Users can only access their own profile
  if (userId !== authenticatedUserId && !checkPermission(userRole, 'read:users')) {
    return res.status(403).json({ error: 'Forbidden' });
  }
  
  const profile = await getUserProfile(userId);
  res.json(profile);
});

Implement resource-level access logging to detect and prevent unauthorized access attempts. Log every access attempt with user ID, resource ID, timestamp, and outcome:

async function logAccessAttempt(userId, resourceType, resourceId, outcome, ip) {
  await db.query(
    'INSERT INTO access_logs (user_id, resource_type, resource_id, outcome, ip_address, timestamp) VALUES ($1, $2, $3, $4, $5, NOW())',
    [userId, resourceType, resourceId, outcome, ip]
  );
}

// Log access in middleware
function authorizeAndLog(resourceType) {
  return async (req, res, next) => {
    const userId = req.userId;
    const resourceId = req.params.resourceId;
    const ip = req.ip;
    
    let hasAccess = false;
    let outcome = 'denied';
    
    switch (resourceType) {
      case 'account':
        hasAccess = await checkAccountOwnership(userId, resourceId);
        outcome = hasAccess ? 'allowed' : 'denied';
        break;
    }
    
    await logAccessAttempt(userId, resourceType, resourceId, outcome, ip);
    
    if (!hasAccess) {
      return res.status(404).json({ error: 'Resource not found' });
    }
    next();
  };
}

Use database constraints to enforce authorization at the data layer. Create database views or stored procedures that automatically filter data based on user context:

CREATE VIEW user_accounts AS
SELECT * FROM accounts
WHERE user_id = current_setting('app.current_user_id', true);

-- In application code
app.get('/api/accounts', authenticateBasicAuth, async (req, res) => {
  const userId = req.userId;
  await set_config('app.current_user_id', userId);
  const accounts = await db.query('SELECT * FROM user_accounts');
  res.json(accounts.rows);
});

Implement comprehensive testing for authorization logic. Create test cases that verify users cannot access resources they don't own:

describe('Authorization', () => {
  it('should prevent access to other users accounts', async () => {
    const user1 = await createUser('user1', 'password1');
    const user2 = await createUser('user2', 'password2');
    
    const account1 = await createAccount(user1.id);
    const account2 = await createAccount(user2.id);
    
    // Attempt to access user2's account with user1's credentials
    const response = await request(app)
      .get(`/api/accounts/${account2.id}`)
      .set('Authorization', basicAuthHeader('user1', 'password1'));
    
    expect(response.status).toBe(404);
  });
});

Consider implementing API gateway-level authorization for centralized control. Many API gateways support Basic Auth integration with authorization policies that can be applied globally:

# API Gateway configuration (Kong example)
plugins:
  - name: basic-auth
    config:
      hide_credentials: true
  
  - name: acl
    config:
      whitelist: ['admin', 'user']
      url: 'http://authorization-service/has-permission'

Finally, implement security headers and rate limiting to complement authorization controls. While not directly related to IDOR, these measures help prevent enumeration and brute-force attacks:

app.use((req, res, next) => {
  res.setHeader('X-Content-Type-Options', 'nosniff');
  res.setHeader('X-Frame-Options', 'DENY');
  res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin');
  next();
});

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

Why is Basic Auth particularly vulnerable to IDOR attacks?

Basic Auth is particularly vulnerable to IDOR attacks because it provides only authentication without built-in authorization controls. The credentials are sent with every request in a stateless manner, making it easy for attackers to enumerate resources by simply modifying URL parameters. Additionally, Basic Auth's simplicity often leads developers to assume that successful authentication grants access to all resources, causing them to skip implementing proper authorization checks.

How does middleBrick detect IDOR vulnerabilities in Basic Auth APIs?

middleBrick detects IDOR vulnerabilities in Basic Auth APIs by automatically extracting authentication credentials from captured requests and then systematically testing authenticated endpoints with modified resource identifiers. The scanner maintains the authenticated context while attempting to access different resources, checking whether the API properly validates ownership. It tests sequential IDs, UUID patterns, and other identifier formats to identify authorization bypasses that would allow attackers to access other users' data.