10 KiB
name, description
| name | description |
|---|---|
| nestjs | Security testing playbook for NestJS applications covering guards, pipes, decorators, module boundaries, and multi-transport auth |
NestJS
Security testing for NestJS applications. Focus on guard gaps across decorator stacks, validation pipe bypasses, module boundary leaks, and inconsistent auth enforcement across HTTP, WebSocket, and microservice transports.
Attack Surface
Decorator Pipeline
- Guards:
@UseGuards,CanActivate, execution context (HTTP/WS/RPC),Reflectormetadata - Pipes:
ValidationPipe(whitelist, transform, forbidNonWhitelisted),ParseIntPipe, custom pipes - Interceptors: response mapping, caching, logging, timeout — can modify request/response flow
- Filters: exception filters that may leak information
- Metadata:
@SetMetadata,@Public(),@Roles(),@Permissions()
Module System
@Moduleboundaries, provider scoping (DEFAULT/REQUEST/TRANSIENT)- Dynamic modules:
forRoot/forRootAsync, global modules - DI container: provider overrides, custom providers
Controllers & Transports
- REST:
@Controller, versioning (URI/Header/MediaType) - GraphQL:
@Resolver, playground/sandbox exposure - WebSocket:
@WebSocketGateway, gateway guards, room authorization - Microservices: TCP, Redis, NATS, MQTT, gRPC, Kafka — often lack HTTP-level auth
Data Layer
- TypeORM: repositories, QueryBuilder, raw queries, relations
- Prisma:
$queryRaw,$queryRawUnsafe - Mongoose: operator injection,
$where,$regex
Auth & Config
@nestjs/passportstrategies,@nestjs/jwt, session-based auth@nestjs/config, ConfigService,.envfiles@nestjs/throttler, rate limiting with@SkipThrottle
API Documentation
@nestjs/swagger: OpenAPI exposure, DTO schemas, auth schemes
High-Value Targets
- Swagger/OpenAPI endpoints in production (
/api,/api-docs,/api-json,/swagger) - Auth endpoints: login, register, token refresh, password reset, OAuth callbacks
- Admin controllers decorated with
@Roles('admin')— test with user-level tokens - File upload endpoints using
FileInterceptor/FilesInterceptor - WebSocket gateways sharing business logic with HTTP controllers
- Microservice handlers (
@MessagePattern,@EventPattern) — often unguarded - CRUD generators (
@nestjsx/crud) with auto-generated endpoints - Background jobs and scheduled tasks (
@nestjs/schedule) - Health/metrics endpoints (
@nestjs/terminus,/health,/metrics) - GraphQL playground/sandbox in production (
/graphql)
Reconnaissance
Swagger Discovery
GET /api
GET /api-docs
GET /api-json
GET /swagger
GET /docs
GET /v1/api-docs
GET /api/v2/docs
Extract: paths, parameter schemas, DTOs, auth schemes, example values. Swagger may reveal internal endpoints, deprecated routes, and admin-only paths not visible in the UI.
Guard Mapping
For each controller and method, identify:
- Global guards (applied in
main.tsor app module) - Controller-level guards (
@UseGuardson the class) - Method-level guards (
@UseGuardson individual handlers) @Public()or@SkipThrottle()decorators that bypass protection
Key Vulnerabilities
Guard Bypass
Decorator Stack Gaps
- Guards execute: global → controller → method. A method missing
@UseGuardswhen siblings have it is the #1 finding. @Public()metadata causing globalAuthGuardto skip enforcement — check if applied too broadly.- New methods added to existing controllers without inheriting the expected guard.
ExecutionContext Switching
- Guards handling only HTTP context (
getRequest()) may fail silently on WebSocket or RPC, returningtrueby default. - Test same business logic through alternate transports to find context-specific bypasses.
Reflector Mismatches
- Guard reads
SetMetadata('roles', [...])but decorator sets'role'(singular) — guard sees no metadata, defaults to allow. applyDecorators()compositions accidentally overriding stricter guards with permissive ones.
Validation Pipe Exploits
Whitelist Bypass
whitelist: truewithoutforbidNonWhitelisted: true: extra properties silently stripped but may have been processed by earlier middleware/interceptors.- Missing
@Type(() => ChildDto)on nested objects:@ValidateNested()without@Typemeans nested payload is never validated. - Array elements:
@IsArray()doesn't validate elements without@ValidateNested({ each: true })and@Type.
Type Coercion
transform: trueenables implicit coercion: strings → numbers,"true"→true,"null"→null.- Exploit truthiness assumptions in business logic downstream.
Conditional Validation
@ValidateIf()and validation groups creating paths where fields skip validation entirely.
Missing Parse Pipes
@Param('id')withoutParseIntPipe/ParseUUIDPipe— string values reach ORM queries directly.
Auth & Passport
JWT Strategy
- Check
ignoreExpirationis false,algorithmsis pinned (nononeor HS/RS confusion) - Weak
secretOrKeyvalues - Cross-service token reuse when audience/issuer not enforced
Passport Strategy Issues
validate()return value becomesreq.user— if it returns full DB record, sensitive fields leak downstream- Multiple strategies (JWT + session): one may bypass restrictions of the other
- Custom guards returning
truefor unauthenticated as "optional auth"
Timing Attacks
- Plain string comparison instead of bcrypt/argon2 in local strategy
Serialization Leaks
Missing ClassSerializerInterceptor
- If not applied globally,
@Exclude()fields (passwords, internal IDs) returned in responses. @Expose()with groups: admin-only fields exposed when groups not enforced per-request.
Circular Relations
- Eager-loaded TypeORM/Prisma relations exposing entire object graph without careful serialization.
Interceptor Abuse
Cache Poisoning
CacheInterceptorwithout user/tenant identity in cache key — responses from one user served to another.- Test: authenticated request, then unauthenticated request returning cached data.
Response Mapping
- Transformation interceptors may leak internal entity fields if mapping is incomplete.
Module Boundary Leaks
Global Module Exposure
@Global()modules expose all providers to every module without explicit imports.- Sensitive services (admin operations, internal APIs) accessible from untrusted modules.
Config Leaks
forRoot/forRootAsyncconfiguration secrets accessible viaConfigServiceinjection in any module.
Scope Issues
- Request-scoped providers (
Scope.REQUEST) incorrectly scoped as DEFAULT (singleton) — request context leaks across concurrent requests.
WebSocket Gateway
- HTTP guards don't automatically apply to WebSocket gateways —
@UseGuardsmust be explicit. - Authentication deferred from
handleConnectionto message handlers allows unauthenticated message sending. - Room/namespace authorization: users joining rooms they shouldn't access.
@SubscribeMessage()handlers relying on connection-level auth instead of per-message validation.
Microservice Transport
@MessagePattern/@EventPatternhandlers often lack guards (considered "internal").- If transport (Redis, NATS, Kafka) is network-accessible, messages can be injected bypassing all HTTP security.
ValidationPipemay only be configured for HTTP — microservice payloads skip validation.
ORM Injection
TypeORM
QueryBuilderand.query()with template literal interpolation → SQL injection.- Relations: API allowing specification of which relations to load via query params.
Mongoose
- Query operator injection:
{ password: { $gt: "" } }via unsanitized request body. $whereand$regexoperators from user input.
Prisma
$queryRaw/$executeRawwith string interpolation (but not tagged template).$queryRawUnsafeusage.
Rate Limiting
@SkipThrottle()on sensitive endpoints (login, password reset, OTP).- In-memory throttler storage: resets on restart, doesn't work across instances.
- Behind proxy without
trust proxy: all requests share same IP, or header spoofable.
CRUD Generators
- Auto-generated CRUD endpoints may not inherit manual guard configurations.
- Bulk operations (
createMany,updateMany) bypassing per-entity authorization. - Query parameter injection in CRUD libraries:
filter,sort,join,selectexposing unauthorized data.
Bypass Techniques
@Public()/ skip-metadata applied via composed decorators at method level causing global guards to skip viaReflectormetadata checks- Route param pollution:
/users/123?id=456— whichidwins in guards vs handlers? - Version routing: v1 of endpoint may still be registered without the guard added to v2
X-HTTP-Method-Overrideor_methodprocessed by Express before guards- Content-type switching:
application/x-www-form-urlencodedinstead of JSON to bypass JSON-specific validation - Exception filter differences: guard throwing results in generic error that leaks route existence info
Testing Methodology
- Enumerate — Fetch Swagger/OpenAPI, map all controllers, resolvers, and gateways
- Guard audit — Map decorator stack per method: which guards, pipes, interceptors are applied at each level
- Matrix testing — Test each endpoint across: unauth/user/admin × HTTP/WS/microservice
- Validation probing — Send extra fields, wrong types, nested objects, arrays to find pipe gaps
- Transport parity — Same operation via HTTP, WebSocket, and microservice transport
- Module boundaries — Check if providers from one module are accessible without proper imports
- Serialization check — Compare raw entity fields with API response fields
Validation Requirements
- Guard bypass: request to guarded endpoint succeeding without auth, showing guard chain break point
- Validation bypass: payload with extra/malformed fields affecting business logic
- Cross-transport inconsistency: same action authorized via HTTP but exploitable via WebSocket/microservice
- Module boundary leak: accessing provider or data across unauthorized module boundaries
- Serialization leak: response containing excluded fields (passwords, internal metadata)
- IDOR: side-by-side requests from different users showing unauthorized data access
- ORM injection: raw query with user-controlled input returning unauthorized data, or error-based evidence of query structure
- Cache poisoning: response from unauthenticated or different-user request matching a prior authenticated user's cached response