Tenant Isolation Failures
Tenant Isolation Failures at a glance
Overview
Tenant isolation failures occur in multi-tenant SaaS applications when data or operations meant for one customer (tenant) are accessible to another. This is particularly critical because a single vulnerability can simultaneously compromise multiple customers' data.
Common failures include queries not scoped to tenant ID, shared caching without tenant context, background jobs processing data across tenant boundaries, and API responses including data from multiple tenants. Proper tenant isolation requires consistent enforcement at every layer: database queries, cache keys, job queues, and API responses.
Where it occurs
Tenant isolation issues arise from database queries not filtered by tenant_id, cache keys not including tenant context, shared resources (queues, sessions) mixing tenant data, background jobs not preserving tenant context, and admin interfaces allowing cross-tenant operations without proper checks.
Impact
Tenant isolation failures lead to exposure of customer data to competitors, compliance violations under GDPR/HIPAA/SOC2, loss of customer trust and contract terminations, legal liability from data breaches, and reputational damage affecting the entire customer base. A single tenant isolation bug can compromise the entire SaaS business model.
Prevention
Implement tenant ID scoping at the ORM level with default scopes or query interceptors. Include tenant context in all cache keys. Use tenant-aware connection pools or database schemas. Preserve tenant context across asynchronous operations. Implement comprehensive testing for cross-tenant access attempts. Add monitoring and alerting for cross-tenant queries. Use row-level security in databases where available.
Examples
Switch tabs to view language/framework variants.
Database queries missing tenant scoping allow cross-tenant access
Queries don't filter by tenant_id enabling data access across tenants.
const express = require('express');
const { Customer } = require('./models');
const app = express();
app.get('/customers', async (req, res) => {
// BUG: No tenant_id filter
const customers = await Customer.findAll();
res.json(customers);
});- Line 8: No tenant filtering
Missing tenant filters in queries expose data across tenant boundaries.
const express = require('express');
const { Customer } = require('./models');
const app = express();
// Middleware to validate tenant
app.use((req, res, next) => {
req.tenantId = validateTenant(req.headers['x-tenant-id'], req.user);
if (!req.tenantId) return res.status(403).json({ error: 'Invalid tenant' });
next();
});
app.get('/customers', async (req, res) => {
// Always filter by tenant
const customers = await Customer.findAll({
where: { tenant_id: req.tenantId }
});
res.json(customers);
});- Line 7: Tenant validation middleware
- Line 15: Tenant scoping on all queries
Always filter queries by tenant_id and validate tenant in middleware.
Engineer Checklist
-
Scope all database queries by tenant_id from authenticated context
-
Include tenant_id in all cache keys
-
Use tenant-aware connection pools or schemas
-
Preserve tenant context in background jobs and async operations
-
Implement default tenant scoping at ORM level
-
Add automated tests for cross-tenant access attempts
-
Monitor queries for missing tenant filters
-
Use row-level security policies where available
-
Validate tenant_id cannot be manipulated by users
-
Audit admin interfaces for cross-tenant operations
End-to-End Example
A SaaS application allows users from Company A to view data belonging to Company B due to missing tenant scoping.
// Vulnerable: No tenant scoping in query
const express = require('express');
const { Customer } = require('./models');
app.get('/api/customers', authenticateUser, async (req, res) => {
// User is authenticated, but query not scoped to their tenant!
const customers = await Customer.findAll();
return res.json(customers);
});// Secure: Always scope queries by tenant_id
const express = require('express');
const { Customer } = require('./models');
app.get('/api/customers', authenticateUser, async (req, res) => {
const tenantId = req.user.tenantId;
// Query explicitly filtered by authenticated user's tenant
const customers = await Customer.findAll({
where: {
tenant_id: tenantId
}
});
return res.json(customers);
});
// Alternative: Use Sequelize default scope
Customer.addScope('defaultScope', {
where: {
tenant_id: getCurrentTenantId()
}
}, { override: true });Discovery
Test if tenant identifiers can be manipulated to access data belonging to other tenants in multi-tenant applications.
-
1. Enumerate tenant ID format
httpAction
Access own tenant data to understand ID format and structure
Request
GET https://api.example.com/api/customersHeaders:Authorization: Bearer tenant-acme-tokenX-Tenant-ID: acme-corpResponse
Status: 200Body:[ { "id": 1001, "name": "John Doe", "email": "john@acme-corp.com" }, { "id": 1002, "name": "Jane Smith", "email": "jane@acme-corp.com" } ]Artifacts
tenant_id_format_identified customer_data_structure -
2. Test tenant ID manipulation in header
httpAction
Modify X-Tenant-ID header to access another tenant's data
Request
GET https://api.example.com/api/customersHeaders:Authorization: Bearer tenant-acme-tokenX-Tenant-ID: techstartup-incResponse
Status: 200Body:[ { "id": 2001, "name": "Alice Johnson", "email": "alice@techstartup.com" }, { "id": 2002, "name": "Bob Williams", "email": "bob@techstartup.com" }, "... (437 customer records from TechStartup Inc)" ]Artifacts
tenant_isolation_bypass cross_tenant_access customer_data_leak -
3. Test tenant ID in URL parameter
httpAction
Modify tenant parameter in query string
Request
GET https://api.example.com/api/reports?tenant_id=global-industriesHeaders:Authorization: Bearer tenant-acme-tokenResponse
Status: 200Body:{ "tenant": "global-industries", "revenue": 8500000, "customers": 1247, "monthly_recurring_revenue": 450000, "churn_rate": 0.04, "sensitive_metrics": "Full financial dashboard accessible" }Artifacts
financial_data_leak competitor_intelligence cross_tenant_reports
Exploit steps
Attacker systematically enumerates and accesses data from all tenants by manipulating tenant identifiers in requests.
-
1. Enumerate all tenant identifiers
Discover tenant IDs through various endpoints
httpAction
Use error messages, public listings, or enumeration to find tenant IDs
Request
GET https://api.example.com/api/customersHeaders:X-Tenant-ID: invalid-tenant-xyzResponse
Status: 400Body:{ "error": "Invalid tenant ID. Valid tenants: acme-corp, techstartup-inc, global-industries, ...", "note": "Error message helpfully lists all tenant IDs" }Artifacts
tenant_enumeration tenant_id_list attack_surface_mapping -
2. Mass data extraction from all tenants
Iterate through all tenant IDs to extract data
httpAction
Loop through discovered tenant IDs and extract customer/financial data
Request
GET https://api.example.com/api/customersHeaders:Authorization: Bearer tenant-acme-tokenX-Tenant-ID: [iterate: acme-corp, techstartup-inc, global-industries, ...]Response
Status: 200Body:{ "extracted": "Complete customer databases from 847 tenants", "total_records": 2450000, "data_types": "Customer PII, payment info, contracts, internal documents", "estimated_value": "Multi-million dollar data breach affecting all platform tenants" }Artifacts
mass_data_breach multi_tenant_compromise customer_pii payment_information confidential_documents -
3. Modify competitor data for sabotage
Use write access to corrupt competitor tenant data
httpAction
If tenant isolation affects writes, modify competitor's critical data
Request
DELETE https://api.example.com/api/customers/bulkHeaders:Authorization: Bearer tenant-acme-tokenX-Tenant-ID: competitor-corpResponse
Status: 200Body:{ "message": "Deleted 15,243 customer records from competitor-corp tenant", "impact": "Competitor loses all customer data, business operations halt" }Artifacts
data_deletion business_sabotage competitor_damage tenant_data_loss
Specific Impact
Cross-tenant data exposure, compliance violations, and loss of customer trust in the SaaS platform.
Fix
Always scope database queries by tenant_id from the authenticated user's context. Never rely on client-provided tenant identifiers. Consider implementing tenant scoping at the ORM level with default scopes or middleware. Use row-level security in the database where available for defense in depth.
Detect This Vulnerability in Your Code
Sourcery automatically identifies tenant isolation failures vulnerabilities and many other security issues in your codebase.
Scan Your Code for Free