Back to blog

February 2, 2026 2 min read Fateh Mohammed

Access Control Bugs in SaaS Applications

Broken access control is the number one web application risk. Here is how it manifests in multi-tenant SaaS products and how to catch it early.

AppSecEngineeringSaaS

OWASP ranks broken access control as the number one web application security risk. In multi-tenant SaaS applications, the consequences are amplified because a single authorization bug can expose one customer's data to another.

Common access control failures

Missing tenant isolation

// Vulnerable: no tenant check
app.get('/api/documents/:id', async (req, res) => {
  const doc = await db.documents.findById(req.params.id)
  res.json(doc)
})

// Fixed: scoped to authenticated tenant
app.get('/api/documents/:id', async (req, res) => {
  const doc = await db.documents.findOne({
    id: req.params.id,
    tenantId: req.auth.tenantId,
  })
  if (!doc) return res.status(404).end()
  res.json(doc)
})

Every data query must include a tenant scope. Relying on frontend routing to prevent cross-tenant access is not sufficient.

Insecure direct object references (IDOR)

Sequential or predictable IDs make it trivial to enumerate resources. Even with authentication, a user can access resources belonging to other users within the same tenant if authorization is only checked at the tenant level.

Missing role checks on sensitive operations

New endpoints are the most common source of access control gaps. A developer adds an admin endpoint and forgets the role middleware. The endpoint works, passes code review, and ships without authorization.

Detection at the PR level

Automated scanners can detect:

  • New route handlers without authentication middleware.
  • Database queries on user-scoped tables missing a tenant or user filter.
  • Admin-only operations without role verification.

These patterns are identifiable in pull request diffs and are high-confidence signals for real vulnerabilities.

Build access control into the architecture

The most reliable defense is making insecure code harder to write than secure code. Use middleware that injects tenant scope by default. Make unscoped queries require explicit opt-in. Design the framework so the secure path is the default path.