Api Key Exposure in Nestjs with Saml
Api Key Exposure in Nestjs with Saml — how this specific combination creates or exposes the vulnerability
When an API built with NestJS uses SAML for authentication, developers sometimes inadvertently expose API keys through misconfigured endpoints or logging. In a black-box scan, middleBrick tests unauthenticated attack surfaces and checks whether API keys are present in responses or metadata. A common pattern is to protect routes with SAML-based session cookies while allowing unauthenticated access to a status or health endpoint that returns internal configuration, including API keys used for downstream services.
NestJS applications that integrate SAML typically rely on libraries such as passport-saml. If routes guarded by SAML are mistakenly omitted in the security configuration, or if route-level guards are applied inconsistently, an unauthenticated attacker can reach endpoints that return sensitive data. For example, an endpoint intended for debugging might return the full NestJS configuration object, which in turn exposes API keys stored in environment variables or configuration files.
During a scan, middleBrick runs checks including Authentication, Data Exposure, and Unsafe Consumption. It detects whether API keys appear in responses, whether CORS rules are too permissive, and whether metadata or introspection endpoints reveal sensitive information. A typical finding is an unauthenticated endpoint that returns JSON containing keys such as API_KEY or SECRET that should only be accessible to authenticated, authorized services. This becomes particularly risky when those keys have broad permissions in downstream systems.
Another vector arises from improper handling of SAML assertions in NestJS controllers. If the application logs assertions or error details containing keys, or if it echoes metadata into responses, middleBrick’s Data Exposure check will flag this as a finding. The scanner also tests for SSRF-like behaviors where an attacker could leverage exposed endpoints to pivot to internal services, using the SAML context to bypass expected network boundaries.
Because middleBrick compares OpenAPI/Swagger definitions with runtime behavior, it can identify mismatches where documentation claims authentication is required but the endpoint is actually public. This is important for API key exposure: a spec may mark a security scheme as required, while the implementation lacks the corresponding guard, allowing keys to be returned in clear text to anyone who discovers the route.
To illustrate, consider a NestJS controller that exposes a configuration route without proper guards:
@Controller()
export class ConfigController {
@Get('config')
getConfig(): any {
return {
apiKey: process.env.EXTERNAL_API_KEY,
endpoint: process.env.SERVICE_ENDPOINT,
};
}
}
If this route is not protected by SAML middleware, a scanner can retrieve the API key directly. Even when SAML guards are present, inconsistent application across routes or reliance on route-level versus method-level security can leave some paths exposed. middleBrick flags such inconsistencies and provides prioritized remediation guidance.
Saml-Specific Remediation in Nestjs — concrete code fixes
Remediation focuses on ensuring SAML middleware is applied consistently, sensitive data is never returned unauthenticated, and API keys are kept out of responses and logs. middleBrick’s findings include actionable remediation steps that map to OWASP API Top 10 and common misconfigurations in SAML setups.
First, apply SAML authentication globally or at the route level using NestJS guards. The typical setup uses passport-saml with a custom guard. Here is a secure configuration example:
@Injectable()
export class SamlAuthGuard implements CanActivate {
constructor(private readonly reflector: Reflector) {}
canActivate(context: ExecutionContext): boolean {
const isPublic = this.reflector.get<boolean>('public', context.getHandler());
if (isPublic) {
return true;
}
const request = context.switchToHttp().getRequest();
return request.isAuthenticated() && request.user && request.user.nameid;
}
}
@Controller()
@UseGuards(SamlAuthGuard)
export class SecureController {
@Get('data')
getData(): any {
return { message: 'Authenticated access only' };
}
}
@Controller()
export class PublicController {
@Get('status')
@SetMetadata('public', true)
getStatus(): any {
return { ok: true };
}
}
This ensures that unless explicitly marked public, all endpoints require a valid SAML-authenticated session. The guard checks for request.user.nameid, a common SAML attribute, to confirm a successful assertion.
Second, avoid returning API keys or internal configuration from any endpoint. If configuration must be exposed, return only safe, non-sensitive values and ensure the route is either public by design or protected as shown above. Never include keys in logs; configure NestJS to sanitize request and response bodies in production logging.
Third, validate and tighten SAML metadata exposure. An endpoint like /saml/metadata may be needed for IdP configuration, but it should be protected or restricted to known partners. middleBrick checks whether metadata is unnecessarily exposed and flags it as a Data Exposure finding if accessible without authentication.
Example of a restricted metadata endpoint:
@Controller('saml')
export class SamlController {
constructor(private readonly samlService: SamlService) {}
@Get('metadata')
@SetMetadata('public', false)
getMetadata(@Res() response: Response) {
const metadata = this.samlService.getMetadata();
response.set('Content-Type', 'application/xml');
return metadata;
}
}
Ensure SamlAuthGuard or equivalent protection applies unless public is explicitly set. middleBrick’s LLM/AI Security checks also verify that SAML flows do not leak assertions or keys through error messages or verbose logging, which could be harvested by attackers.
Finally, use environment-based configuration to keep API keys outside the codebase and ensure they are not serialized into responses. middleBrick’s Input Validation and Property Authorization checks confirm that only intended data shapes are returned, reducing the chance of accidental key disclosure through debug endpoints.