Bola Idor in Nestjs with Bearer Tokens
BOLA IDOR in Nestjs with Bearer Tokens — how this specific combination creates or exposes the vulnerability
Broken Object Level Authorization (BOLA) occurs when an API exposes an endpoint that accepts user-supplied identifiers (e.g., :id) and uses those identifiers to access resources without verifying that the requesting user is authorized to view or modify that specific object. In NestJS applications that rely on Bearer Tokens for authentication, BOLA can arise when authorization checks are incomplete or inconsistent, even though an access token is present and accepted.
Consider a typical setup where JWT-based Bearer Tokens identify a user (payload containing sub: user_id) and an endpoint like GET /users/:userId is implemented with minimal guards. If the guard only confirms that a valid Bearer Token exists but does not compare the token’s user identifier with the :userId path parameter, an attacker can change :userId to access or manipulate other users’ data. This is BOLA because the object (the user resource) is accessible by ID without proper ownership or role-based authorization.
NestJS itself does not introduce BOLA; the risk comes from how endpoints are wired after authentication. Common patterns that enable BOLA include:
- Using a global or route-scoped guard that validates the Bearer Token but skips a granular policy check (e.g., CanActivate that only verifies token validity, not resource ownership).
- Relying on frontend-supplied IDs without server-side reconciliation between the token identity and the requested resource.
- Inconsistent use of IDs across services (e.g., using UUIDs in one service and sequential IDs in another) leading to confused deputy scenarios where an ID from one context is trusted in another.
Real-world examples include endpoints such as /projects/:projectId or /invoices/:invoiceId where an attacker iterates over IDs and reads data belonging to other tenants or users. Because Bearer Tokens are often used across multiple services, a compromised or overly permissive token amplifies BOLA risks, allowing lateral movement across resources that should be isolated.
To detect this pattern during a scan, middleBrick runs checks that compare authentication context with object-level authorization by analyzing endpoint definitions and runtime responses. When OpenAPI specs are provided, middleBrick resolves $ref chains and cross-references paths with security schemes to identify endpoints that accept user-controlled IDs without explicit ownership constraints. This helps surface BOLA risks even in unauthenticated scans where tokens are supplied to test whether endpoints enforce proper authorization at the object level.
Bearer Tokens-Specific Remediation in Nestjs — concrete code fixes
Remediation focuses on ensuring that every object-level operation validates the relationship between the authenticated user (from the Bearer Token) and the requested resource. Below are concrete, idiomatic NestJS patterns and code examples to prevent BOLA when using Bearer Tokens.
1. Use a dedicated AuthGuard that extracts and attaches user identity
Ensure your JWT auth guard validates the Bearer Token and attaches a normalized user payload to the request context. This makes identity available to subsequent authorization logic.
@Injectable()
export class JwtAuthGuard implements CanActivate {
constructor(private readonly authService: AuthService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
try {
const token = this.extractToken(request);
const payload = await this.authService.validateToken(token);
request.user = payload; // { sub: 'user-uuid', roles: ['user'] }
return true;
} catch {
return false;
}
}
private extractToken(req: Request): string | undefined {
const header = req.headers['authorization'];
if (header && header.toLowerCase().startsWith('bearer ')) {
return header.slice(7).trim();
}
return undefined;
}
}
2. Enforce object-level ownership in a custom CanActivate or a dedicated policy
Do not rely on route parameters alone. Before allowing access, compare the user identity from the token with the resource’s owning user_id (or tenant_id).
@Injectable()
export class ProjectOwnershipGuard implements CanActivate {
constructor(private readonly projectsService: ProjectsService) {}
async canActivate(context: ExecutionContext): Promise<boolean> {
const request = context.switchToHttp().getRequest();
const user = request.user;
const projectId = request.params.projectId;
if (!user || !projectId) {
return false;
}
const project = await this.projectsService.findOne(projectId);
if (!project) {
return false;
}
// Ensure the requesting user is the project owner or has explicit access
return project.ownerId === user.sub;
}
}
3. Apply guards at the controller or route level and avoid public IDs when possible
Use NestJS’s decorators to bind authorization tightly to routes. Avoid exposing sequential or guessable IDs; prefer opaque identifiers (UUIDs) and map them to internal keys after ownership checks.
@Controller('projects')
@UseGuards(JwtAuthGuard, ProjectOwnershipGuard)
export class ProjectsController {
constructor(private readonly projectsService: ProjectsService) {}
@Get(':projectId')
findOne(@Param('projectId') projectId: string): Promise<Project> {
return this.projectsService.findOneWithAccess(projectId);
}
}
4. Example Bearer Token usage in requests
Clients must include the Bearer Token in the Authorization header. This example shows valid usage where the token identity matches the requested resource.
GET /projects/abc-123 HTTP/1.1
Host: api.example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyLWMxMjMiLCJyb2xlcyI6WyJ1c2VyIl19.signature
5. Complementary practices
- Scope tokens with minimal claims and avoid embedding object-level permissions directly in the token unless you can rotate/revoke tokens efficiently.
- Log authorization failures without exposing sensitive path or ID details to reduce information leakage.
- Use middleware to normalize IDs (e.g., resolve route IDs to database keys) before handlers run, ensuring handlers only deal with verified references.
Related CWEs: bolaAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-250 | Execution with Unnecessary Privileges | HIGH |
| CWE-639 | Insecure Direct Object Reference | CRITICAL |
| CWE-732 | Incorrect Permission Assignment | HIGH |