HIGH broken access controlbasic auth

Broken Access Control with Basic Auth

How Broken Access Control Manifests in Basic Auth

Broken Access Control in Basic Auth systems often stems from improper session handling and credential validation. Since Basic Auth transmits credentials in base64 encoding with every request, any logic error in authentication middleware can expose sensitive endpoints to unauthorized users.

The most common Basic Auth access control failure occurs when developers rely solely on client-side headers without server-side validation. Consider this vulnerable Node.js Express pattern:

app.get('/admin', (req, res) => {
  // Only checks if header exists, not if it's valid
  if (req.headers.authorization) {
    res.json({ data: 'sensitive admin data' });
  } else {
    res.status(401).send('Unauthorized');
  }
});

This code grants access to anyone who includes an Authorization header, regardless of whether the credentials are valid. The proper approach requires verifying the decoded credentials against a user store:

const users = { admin: 'correct-password' };

app.get('/admin', (req, res) => {
  const authHeader = req.headers.authorization;
  if (!authHeader) {
    return res.status(401).send('Missing Authorization header');
  }

  const [type, credentials] = authHeader.split(' ');
  if (type !== 'Basic') {
    return res.status(401).send('Invalid auth type');
  }

  const decoded = Buffer.from(credentials, 'base64').toString();
  const [username, password] = decoded.split(':');

  if (!users[username] || users[username] !== password) {
    return res.status(401).send('Invalid credentials');
  }

  // Now we know the user is authenticated
  res.json({ data: 'sensitive admin data' });
});

Another critical failure pattern involves role-based access control without proper validation. Many Basic Auth implementations store user roles in plaintext and fail to check permissions:

const users = {
  admin: { password: 'admin-pass', role: 'admin' },
  user: { password: 'user-pass', role: 'user' }
};

app.get('/admin', (req, res) => {
  const [username] = validateBasicAuth(req);
  // BUG: Doesn't verify role before granting access
  res.json({ data: 'admin data' });
});

function validateBasicAuth(req) {
  const authHeader = req.headers.authorization;
  if (!authHeader) return null;
  
  const [type, credentials] = authHeader.split(' ');
  if (type !== 'Basic') return null;
  
  const decoded = Buffer.from(credentials, 'base64').toString();
  const [username, password] = decoded.split(':');
  
  if (!users[username] || users[username].password !== password) {
    return null;
  }
  
  return [username];
}

Resource-level access control failures are particularly dangerous in Basic Auth systems. Developers often forget to validate that users can only access their own resources:

app.get('/users/:id', (req, res) => {
  const authUser = validateBasicAuth(req);
  if (!authUser) {
    return res.status(401).send('Unauthorized');
  }
  
  // BUG: Doesn't verify user owns this resource
  const userId = req.params.id;
  const user = db.getUserById(userId);
  res.json(user);
});

This allows any authenticated user to view any other user's data by changing the ID parameter. The fix requires ownership verification:

app.get('/users/:id', (req, res) => {
  const authUser = validateBasicAuth(req);
  if (!authUser) {
    return res.status(401).send('Unauthorized');
  }
  
  const userId = req.params.id;
  const user = db.getUserById(userId);
  
  // Verify ownership
  if (user.username !== authUser) {
    return res.status(403).send('Forbidden');
  }
  
  res.json(user);
});

Time-based access control is another area where Basic Auth implementations fail. Some systems need to restrict access during maintenance windows but implement this incorrectly:

app.get('/maintenance-mode', (req, res) => {
  const now = new Date();
  const isMaintenance = now.getHours() >= 2 && now.getHours() < 4;
  
  if (isMaintenance) {
    // BUG: Returns 200 with maintenance message instead of 403
    return res.status(200).json({ message: 'System under maintenance' });
  }
  
  res.json({ data: 'normal operation' });
});

This approach doesn't actually prevent access—it just changes the response content. Proper implementation should return 403 Forbidden during restricted periods.

Basic Auth-Specific Detection

Detecting Broken Access Control in Basic Auth systems requires both automated scanning and manual verification. The most effective approach combines runtime testing with specification analysis.

Automated detection starts with identifying Basic Auth endpoints through header analysis. A proper scanner looks for:

Authorization: Basic dXNlcm5hbWU6cGFzc3dvcmQ=

Then systematically tests each endpoint with various credential combinations:

const testCases = [
  { desc: 'No credentials', headers: {} },
  { desc: 'Invalid credentials', headers: { 'Authorization': 'Basic invalid' } },
  { desc: 'Valid credentials, wrong role', headers: { 'Authorization': 'Basic YWRtaW46cGFzczEyMw==' } },
  { desc: 'Valid credentials, correct role', headers: { 'Authorization': 'Basic YWRtaW46YWRtaW4xMjM=' } }
];

middleBrick's approach to Basic Auth detection includes 12 parallel security checks that specifically target authentication bypass scenarios. The scanner tests for common vulnerabilities like:

  • Missing authentication requirements on sensitive endpoints
  • Credential stuffing resistance
  • Rate limiting bypass opportunities
  • Information disclosure through error messages
  • Timing attacks that reveal valid usernames

For OpenAPI/Swagger specification analysis, middleBrick cross-references documented security requirements with actual runtime behavior. This catches discrepancies where the spec says "Basic Auth required" but the implementation allows anonymous access.

Manual testing should focus on these critical areas:

// Test for IDOR vulnerabilities
const testUserId = 1;
const authUser = 'victim-user';
const victimUrl = `/api/users/${testUserId}/data`;

// Try accessing another user's data
const response = await fetch(victimUrl, {
  headers: { 'Authorization': 'Basic ' + btoa('attacker:attacker-pass') }
});

if (response.status === 200) {
  console.log('IDOR vulnerability detected');
}

Role-based access control testing requires enumerating all user roles and testing each endpoint with every role combination. This ensures that lower-privileged users cannot access admin-only functionality.

Time-based access control testing involves scheduling requests during different time windows to verify that restrictions are properly enforced. This includes testing daylight saving time transitions and timezone handling.

middleBrick's CLI tool makes this detection process straightforward:

npx middlebrick scan https://api.example.com --auth-type basic --username admin --password admin-pass

The GitHub Action integration allows continuous monitoring of Basic Auth endpoints:

name: API Security Scan
on: [push, pull_request]

jobs:
  security:
    runs-on: ubuntu-latest
    steps:
    - uses: actions/checkout@v2
    - name: Run middleBrick scan
      run: |
        npx middlebrick scan https://staging-api.example.com \
          --auth-type basic \
          --username ${{ secrets.BASIC_AUTH_USER }} \
          --password ${{ secrets.BASIC_AUTH_PASS }}

This setup ensures that any changes introducing Broken Access Control are caught before deployment to production.

Basic Auth-Specific Remediation

Remediating Broken Access Control in Basic Auth systems requires implementing proper authentication middleware and access control checks. The foundation is a robust authentication function that validates credentials and extracts user context:

const crypto = require('crypto');
const users = require('./user-database');

function authenticateBasic(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader) {
    return res.status(401).json({ error: 'Missing Authorization header' });
  }

  const [type, credentials] = authHeader.split(' ');
  if (type !== 'Basic') {
    return res.status(401).json({ error: 'Invalid auth type' });
  }

  const decoded = Buffer.from(credentials, 'base64').toString();
  const [username, password] = decoded.split(':');

  if (!username || !password) {
    return res.status(401).json({ error: 'Invalid credentials format' });
  }

  const user = users.findByUsername(username);
  if (!user) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Use constant-time comparison to prevent timing attacks
  const passwordMatch = crypto.timingSafeEqual(
    Buffer.from(password),
    Buffer.from(user.password)
  );

  if (!passwordMatch) {
    return res.status(401).json({ error: 'Invalid credentials' });
  }

  // Attach user context to request for downstream middleware
  req.user = { 
    id: user.id, 
    username: user.username, 
    role: user.role 
  };

  next();
}

This middleware should be applied to all protected routes using Express's app.use or router.use patterns:

const authRouter = express.Router();
authRouter.use(authenticateBasic);

authRouter.get('/admin', (req, res) => {
  if (req.user.role !== 'admin') {
    return res.status(403).json({ error: 'Admin access required' });
  }
  res.json({ data: 'admin dashboard' });
});

Resource-level access control requires ownership verification middleware:

function verifyOwnership(resourceType) {
  return async (req, res, next) => {
    const resourceId = req.params.id;
    const resource = await db.getResource(resourceType, resourceId);
    
    if (!resource) {
      return res.status(404).json({ error: 'Resource not found' });
    }
    
    if (resource.ownerId !== req.user.id) {
      return res.status(403).json({ error: 'Access denied' });
    }
    
    req.resource = resource;
    next();
  };
}

// Usage
app.get('/users/:id/profile', 
  authenticateBasic, 
  verifyOwnership('user'), 
  (req, res) => {
    res.json(req.resource.profile);
  }
);

Role-based access control should use a centralized permission system:

const permissions = {
  admin: ['read', 'write', 'delete', 'admin-dashboard'],
  user: ['read', 'write'],
  viewer: ['read']
};

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

// Usage
app.get('/reports', 
  authenticateBasic, 
  checkPermission('read'), 
  (req, res) => {
    res.json({ reports: 'financial reports' });
  }
);

Time-based access control can be implemented with middleware that checks current time against allowed windows:

function restrictByTime(allowedHours) {
  return (req, res, next) => {
    const now = new Date();
    const currentHour = now.getHours();
    const isAllowed = allowedHours.some(range => 
      currentHour >= range.start && currentHour < range.end
    );
    
    if (!isAllowed) {
      return res.status(403).json({ 
        error: 'Access restricted during this time period' 
      });
    }
    next();
  };
}

// Usage: allow access 9 AM - 5 PM
app.get('/office-hours', 
  authenticateBasic, 
  restrictByTime([{ start: 9, end: 17 }]),
  (req, res) => {
    res.json({ message: 'Welcome to office hours' });
  }
);

Input validation is critical for preventing injection attacks through Basic Auth credentials:

function sanitizeInput(input) {
  // Remove any non-alphanumeric characters except common username chars
  return input.replace(/[^a-zA-Z0-9._-]/g, '');
}

// Usage in authentication
const [username, password] = decoded.split(':').map(sanitizeInput);

Rate limiting should be applied at the authentication level to prevent brute force attacks:

const rateLimit = require('express-rate-limit');

const authLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // limit each IP to 5 requests per windowMs
  message: 'Too many authentication attempts'
});

// Apply rate limiting to authentication routes
app.post('/login', authLimiter, (req, res) => {
  // authentication logic
});

middleBrick's remediation guidance provides specific recommendations for each vulnerability type, mapping findings to OWASP API Security Top 10 controls and compliance frameworks like PCI-DSS and SOC2.

Frequently Asked Questions

Why is Basic Auth particularly vulnerable to Broken Access Control?
Basic Auth transmits credentials with every request in base64 encoding, making it easy to intercept or analyze traffic. The stateless nature means each request must be independently validated, increasing the attack surface. Developers often implement incomplete authentication checks, assuming the presence of an Authorization header is sufficient. The simplicity of Basic Auth also leads to over-reliance on client-side validation rather than server-side enforcement.
How does middleBrick detect Basic Auth Broken Access Control?
middleBrick uses a combination of black-box scanning and specification analysis. It identifies Basic Auth endpoints by detecting Authorization headers, then systematically tests each endpoint with various credential combinations. The scanner checks for missing authentication requirements, role-based access bypass, resource ownership verification failures, and time-based access control weaknesses. middleBrick also analyzes OpenAPI specifications to identify discrepancies between documented security requirements and actual runtime behavior.