HIGH broken access controlkoa

Broken Access Control in Koa

How Broken Access Control Manifests in Koa

Broken Access Control in Koa applications often emerges through subtle middleware patterns that bypass authentication checks. A common vulnerability occurs when developers place authorization middleware after route handlers, allowing unauthenticated requests to reach protected endpoints before being blocked.

// Vulnerable pattern
router.get('/api/users/:id', async (ctx) => {
  const user = await User.findById(ctx.params.id);
  ctx.body = user;
});

// Authorization middleware placed AFTER the route
router.use(authenticate);
router.use(authorize);

In this Koa-specific anti-pattern, requests hit the route handler first, potentially exposing sensitive data before authentication middleware executes. The correct pattern requires wrapping route handlers with authorization checks:

router.get('/api/users/:id', authenticate, authorize, async (ctx) => {
  const user = await User.findById(ctx.params.id);
  if (ctx.state.user.id !== user.id) {
    ctx.status = 403;
    ctx.body = { error: 'Forbidden' };
    return;
  }
  ctx.body = user;
});

Another Koa-specific manifestation involves improper use of ctx.state for user context. When authentication middleware fails silently or doesn't set ctx.state.user, subsequent middleware may operate with undefined user data:

// Vulnerable: assumes ctx.state.user always exists
router.post('/api/admin', async (ctx) => {
  if (!ctx.state.user.isAdmin) {
    ctx.status = 403;
    return;
  }
  // Admin logic here
});

// Better: explicit authentication check
router.post('/api/admin', authenticate, async (ctx) => {
  if (!ctx.state.user || !ctx.state.user.isAdmin) {
    ctx.status = 403;
    return;
  }
  // Admin logic here
});

Property-level authorization vulnerabilities also plague Koa apps. Developers often forget to validate that users can only access their own resources:

// Vulnerable: IDOR in Koa
router.get('/api/orders/:orderId', async (ctx) => {
  const order = await Order.findById(ctx.params.orderId);
  ctx.body = order; // Any authenticated user can access any order
});

// Secure: validate resource ownership
router.get('/api/orders/:orderId', authenticate, async (ctx) => {
  const order = await Order.findById(ctx.params.orderId);
  if (!order || order.userId !== ctx.state.user.id) {
    ctx.status = 404;
    return;
  }
  ctx.body = order;
});

Koa-Specific Detection

Detecting Broken Access Control in Koa applications requires examining middleware execution order and authorization patterns. middleBrick's black-box scanning approach is particularly effective for Koa APIs since it tests the actual runtime behavior without needing source code access.

When scanning a Koa API endpoint, middleBrick tests for common Koa-specific vulnerabilities by sending authenticated and unauthenticated requests to protected endpoints. The scanner identifies if authentication middleware is properly positioned and whether authorization checks are consistently applied.

# Scan a Koa API endpoint
middlebrick scan https://api.example.com/v1/users/123

The scan results reveal whether the endpoint properly enforces access controls. middleBrick tests for:

  • Authentication bypass: Can unauthenticated requests access protected endpoints?
  • Privilege escalation: Can regular users access admin functionality?
  • Resource manipulation: Can users modify others' data by changing IDs?
  • Authorization bypass: Do authorization checks properly validate user permissions?

For Koa applications, middleBrick specifically checks for patterns like missing ctx.state.user validation, improper middleware ordering, and inconsistent authorization across similar endpoints. The scanner provides a security score (0-100) with detailed findings including the exact requests that bypassed security controls.

Developers can integrate middleBrick into their Koa development workflow using the CLI or GitHub Action:

# GitHub Action for Koa API security
- name: Scan Koa API
  uses: middlebrick/middlebrick-action@v1
  with:
    target_url: ${{ secrets.API_URL }}
    fail_below: 80

This ensures every Koa API change is automatically tested for Broken Access Control vulnerabilities before deployment.

Koa-Specific Remediation

Remediating Broken Access Control in Koa requires implementing robust middleware patterns and consistent authorization checks. The foundation is a reliable authentication middleware that always sets ctx.state.user or explicitly fails:

const jwt = require('jsonwebtoken');

async function authenticate(ctx, next) {
  const authHeader = ctx.headers.authorization;
  if (!authHeader) {
    ctx.status = 401;
    ctx.body = { error: 'Authorization header missing' };
    return;
  }

  const token = authHeader.replace('Bearer ', '');
  try {
    const user = jwt.verify(token, process.env.JWT_SECRET);
    ctx.state.user = user;
    await next();
  } catch (err) {
    ctx.status = 401;
    ctx.body = { error: 'Invalid token' };
  }
}

For authorization, create reusable middleware that validates permissions:

function authorize(requiredRole) {
  return async (ctx, next) => {
    if (!ctx.state.user) {
      ctx.status = 401;
      return;
    }

    if (requiredRole && !ctx.state.user.roles.includes(requiredRole)) {
      ctx.status = 403;
      return;
    }

    await next();
  };
}

// Usage in Koa routes
router.get('/api/admin', authenticate, authorize('admin'), async (ctx) => {
  ctx.body = { message: 'Admin access granted' };
});

Implement property-level authorization using Koa's parameter middleware:

async function validateResourceOwnership(ctx, next) {
  const resourceId = ctx.params.resourceId;
  const resource = await Resource.findById(resourceId);
  
  if (!resource || resource.ownerId !== ctx.state.user.id) {
    ctx.status = 404;
    return;
  }
  
  ctx.state.resource = resource;
  await next();
}

// Route with ownership validation
router.get('/api/resources/:resourceId', 
  authenticate, 
  validateResourceOwnership, 
  async (ctx) => {
    ctx.body = ctx.state.resource;
  }
);

For comprehensive protection, use Koa's router.param() to automatically validate resource ownership for all routes with specific parameters:

router.param('userId', async (userId, ctx, next) => {
  const user = await User.findById(userId);
  if (!user || user.id !== ctx.state.user.id) {
    ctx.status = 404;
    return;
  }
  ctx.state.userResource = user;
  await next();
});

// All routes with :userId now have automatic validation
router.get('/api/users/:userId/profile', authenticate, async (ctx) => {
  ctx.body = ctx.state.userResource;
});

Frequently Asked Questions

How does Broken Access Control differ between Koa and Express?
Koa's middleware execution model (async/await) and ctx.state for context sharing create different vulnerability patterns than Express. Koa's cleaner async flow can lead to developers forgetting explicit authentication checks, while Express's callback patterns often force more defensive programming. The core concepts are identical, but implementation details matter.
Can middleBrick detect if my Koa API has proper middleware ordering?
Yes. middleBrick's black-box scanning tests whether authentication middleware executes before route handlers by attempting authenticated and unauthenticated requests. The scanner identifies if protected endpoints are accessible without proper authentication, revealing middleware ordering issues that would allow Broken Access Control.