HIGH api key exposureexpresssaml

Api Key Exposure in Express with Saml

Api Key Exposure in Express with Saml — how this specific combination creates or exposes the vulnerability

When an Express application uses SAML for authentication, developers often focus on the correctness of the SAML flow—issuer, destination URLs, certificate validation, and session management—while overlooking how API keys are handled in the same service. If an Express server exposes API keys through logs, error messages, HTTP headers, or debug endpoints, and also consumes SAML assertions to authorize access, the combination can inadvertently link a privileged SAML-authenticated session to an insecure API key handling path.

In this context, 'Api Key Exposure' refers to any situation where an API key (e.g., a service-to-service credential used to call downstream APIs) is transmitted in plaintext, stored insecurely, or reflected in responses. Common exposure vectors include:

  • Logging the raw Authorization header or API key values during SAML assertion processing.
  • Returning API keys in JSON error responses when SAML-based authentication fails or redirects incorrectly.
  • Including API keys in URLs or query parameters that may be recorded in server access logs.
  • Using SAML attributes to dynamically construct API requests without sanitizing or protecting the resulting key material.

An attacker who can influence or observe SAML authentication outcomes (e.g., by manipulating NameID or session index) might trigger code paths where an Express route sends an API key to a downstream service. If the request is not properly isolated and keys are not protected in memory and in transit, the key may be leaked via logs, error pages, or even through cross-site scripting if the key is reflected into HTML. Because SAML assertions often carry user identity and group information used to make authorization decisions, a leaked API key can be tied to a specific authenticated identity, amplifying the impact.

Consider an Express route that accepts a SAML-validated user and then calls a third-party service using an API key stored in process configuration. If the route does not validate the SAML assertion’s attributes strictly (for example, failing to check audience or issuer), and then logs the outgoing request including the key, an authenticated attacker with a maliciously crafted SAML response could cause the key to be written to logs. This illustrates how improper handling of SAML identity combined with poor API key hygiene leads to exposure. The risk is not in SAML itself, but in how Express code manages secrets and reflects identity information.

To detect such issues, scanning tools like middleBrick analyze the unauthenticated attack surface of an Express endpoint, looking for places where API keys appear in responses or error flows and correlating them with SAML-consuming routes identified through OpenAPI specs and runtime behavior. Findings typically highlight missing input validation on SAML attributes, insecure logging, and missing transport protections as high-severity items. Remediation focuses on strict assertion validation, isolating key usage from identity context, and ensuring keys are never reflected to the client or logged in plaintext.

Saml-Specific Remediation in Express — concrete code fixes

Secure Express applications that consume SAML must ensure API keys are handled independently of identity assertions and are never exposed through the request/response pipeline. Below are concrete practices and code examples to reduce exposure risk.

1. Validate and sanitize SAML assertions before using them to build API requests

Always verify issuer, audience, and signature before reading attributes. Do not use raw SAML attributes to construct URLs or headers that include secrets.

const SamlStrategy = require('passport-saml').Strategy;
const https = require('https');

passport.use(new SamlStrategy({
  entryPoint: 'https://idp.example.com/sso',
  issuer: 'https://app.example.com',
  cert: process.env.SAML_CERT,
  decryptionPvk: process.env.SAML_DECRYPT_KEY,
  identifierFormat: 'urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress',
  validateInResponseTo: false
}, function(profile, done) {
  // Validate required attributes
  if (!profile.email || !profile.groups) {
    return done(new Error('Missing SAML attributes'), null);
  }
  // Do NOT use profile to build API keys or secrets
  return done(null, { email: profile.email, groups: profile.groups });
}));

2. Store API keys outside the request context and avoid logging

Load API keys from environment variables or a secrets manager at startup. Ensure they are not attached to the request or response objects, and never log them.

// At startup, load key once
const downstreamApiKey = process.env.DOWNSTREAM_API_KEY;

app.post('/api/impersonate', passport.authenticate('saml', { session: false }), (req, res) => {
  // req.user comes from SAML profile, not containing API keys
  if (!downstreamApiKey) {
    return res.status(500).json({ error: 'Server configuration error' });
  }
  const options = {
    hostname: 'api.partner.com',
    path: '/v1/data',
    method: 'GET',
    headers: {
      'Authorization': `Bearer ${downstreamApiKey}`,
      'X-User-Email': req.user.email
    }
  };
  https.get(options, (proxyRes) => {
    let data = '';
    proxyRes.on('data', chunk => data += chunk);
    proxyRes.on('end', () => {
      res.json({ data });
    });
  }).on('error', (err) => {
    // Do NOT log the API key
    console.error('Downstream call failed:', err.message);
    res.status(502).json({ error: 'Upstream failure' });
  });
});

3. Use strict transport and avoid exposing keys in URLs or query strings

Always use HTTPS for downstream calls and avoid putting keys in URLs. If query parameters are required, ensure they are short-lived and not logged by your infrastructure.

const qs = require('querystring');

function callPartnerApi(userToken) {
  const key = process.env.DOWNSTREAM_API_KEY;
  const url = `https://api.partner.com/v2/resource?${qs.stringify({ token: userToken })}`;
  // Prefer headers over query for secrets; if key must be in query, use one-time tokens
  const options = {
    hostname: 'api.partner.com',
    path: `/v2/resource?key=${encodeURIComponent(key)}` // avoid this pattern; shown for illustration only
  };
  return https.get(options, (res) => { /* handle response */ });
}

4. Harden error handling to prevent key leakage

Ensure error responses do not include stack traces, headers, or configuration values that could reveal API keys. Sanitize messages returned to the client.

app.use((err, req, res, next) => {
  // Never send raw errors that might contain keys or internal paths
  console.error('Unhandled error:', err && err.message ? err.message : err);
  res.status(500).json({ error: 'Internal server error' });
});

5. Apply principle of least privilege to downstream keys

Use distinct API keys per SAML role or tenant, and rotate them regularly. Do not share a single key across multiple SAML identities or applications.

// Example mapping (simplified)
const roleKeyMap = {
  'urn:oid:1.3.6.1.4.1.5923.1.1.1.1': process.env.ROLE_A_KEY,
  'urn:oid:1.3.6.1.4.1.5923.1.1.1.2': process.env.ROLE_B_KEY
};

function getKeyForSamlRole(roleUri) {
  return roleKeyMap[roleUri] || null;
}

Frequently Asked Questions

Can middleBrick detect exposed API keys in SAML-based Express applications?
Yes. middleBrick scans the unauthenticated attack surface of an Express endpoint, including routes that consume SAML assertions, and checks for API key exposure in responses, logs, and error flows by analyzing runtime behavior alongside OpenAPI/Swagger specifications with full $ref resolution.
Does middleBrick fix API key exposure findings in Express with SAML?
No. middleBrick detects and reports findings with severity and remediation guidance, but it does not fix, patch, block, or remediate. Developers must apply secure coding practices and rotate keys based on the provided guidance.