HIGH api key exposuresailsoauth2

Api Key Exposure in Sails with Oauth2

Api Key Exposure in Sails with Oauth2 — how this specific combination creates or exposes the vulnerability

When an API built with Sails.js relies on OAuth 2.0 for authorization but exposes an API key as a secondary credential, the combination can unintentionally widen the attack surface. OAuth 2.0 is designed to delegate access using short-lived tokens, whereas API keys are long-lived secrets intended for service-to-service identification. If both are accepted by the same endpoint, an attacker who steals or intercepts the API key can bypass the token-centric protections that OAuth 2.0 provides.

In Sails, this often occurs when controllers check for an API key in headers (e.g., x-api-key) alongside OAuth 2.0 bearer tokens. Because the API key is static and rarely rotated, it becomes a high-value target. If logs, error messages, or client-side code inadvertently reveal the key, an attacker can reuse it to call OAuth-protected endpoints without needing a valid token. This is especially risky if the API key is embedded in JavaScript bundles or mobile binaries that are publicly accessible.

The vulnerability is compounded when OAuth 2.0 scopes are broad or misconfigured. An attacker with an API key might probe endpoints that require specific scopes, relying on Sails middleware that does not strictly enforce scope validation before checking for the API key. This can lead to privilege escalation or unauthorized data access, aligning with BOLA/IDOR and BFLA patterns where object-level permissions are not properly validated.

OAuth 2.0 introduces several grant types, and using the wrong one in Sails can also expose keys. For example, using the client credentials flow for public clients (such as single-page applications) may lead to embedding client secrets in frontend code, effectively turning them into exposed API keys. Similarly, if refresh tokens are stored insecurely in Sails sessions or cookies, they can be stolen and exchanged for new access tokens, bypassing key-based mitigations.

Detection frameworks like middleBrick identify this risk by cross-referencing OpenAPI specs with runtime behavior. If the spec defines both an apiKey security scheme and an oauth2 scheme, but runtime tests show endpoints accepting the API key without validating OAuth tokens, this discrepancy is flagged. Such findings map to OWASP API Top 10 controls and can be tied to compliance requirements in PCI-DSS and SOC2 around credential management.

Oauth2-Specific Remediation in Sails — concrete code fixes

To secure Sails applications using OAuth 2.0, ensure that API keys are not accepted as a substitute for proper token validation. Remove any logic that checks for x-api-key in OAuth-protected endpoints, and enforce token-only access. Below are concrete code examples demonstrating secure OAuth 2.0 integration in Sails.

Using an OAuth2 Strategy with Passport.js

Configure Passport.js to validate bearer tokens and integrate with Sails controllers. This example uses the oauth2orize and passport-oauth2 strategies to validate tokens before allowing access.

// config/passport.js
const passport = require('passport');
const OAuth2Strategy = require('passport-oauth2').Strategy;

passport.use('api-bearer', new OAuth2Strategy({
    authorizationURL: 'https://auth.example.com/oauth/authorize',
    tokenURL: 'https://auth.example.com/oauth/token',
    clientID: 'YOUR_CLIENT_ID',
    clientSecret: 'YOUR_CLIENT_SECRET',
    callbackURL: 'https://api.example.com/auth/callback',
    scope: ['read', 'write']
  },
  function(accessToken, refreshToken, profile, done) {
    // Verify token with your auth server or local introspection
    return done(null, { accessToken, scope: profile.scope });
  }
));

// api/controllers/UserController.js
module.exports = {
  me: function(req, res) {
    if (!req.user) {
      return res.unauthorized('Invalid or missing OAuth2 token');
    }
    return res.ok({ id: req.user.id, scope: req.user.scope });
  }
};

Enforcing Scopes and Token Validation in Controllers

Ensure that each endpoint validates both the presence of a token and the required scopes. This prevents over-privileged access and aligns with the principle of least privilege.

// api/policies/require-oauth-scope.js
module.exports = function requiredScope(requiredScopes) {
  return function(req, res, next) {
    if (!req.user || !req.user.scope) {
      return res.forbidden('Missing OAuth2 scope');
    }
    const userScopes = req.user.scope.split(' ');
    const hasScope = requiredScopes.every(scope => userScopes.includes(scope));
    if (!hasScope) {
      return res.forbidden(`Insufficient scope. Required: ${requiredScopes.join(', ')}`);
    }
    return next();
  };
};

// usage in controller
module.exports = {
  createPayment: [ 
    require('./policies/require-oauth-scope')('payments:write'),
    function(req, res) {
      // Safe to proceed: token and scope validated
      return res.created({ transactionId: 'tx_123' });
    }
  ]
};

Disabling API Key Acceptance in OAuth Endpoints

Explicitly configure Sails to reject API keys on routes protected by OAuth 2.0. This can be done by adjusting policies and ensuring that global middleware does not fall back to key-based checks.

// config/policies.js
module.exports.policies = {
  OAuthController: {
    '*': ['require-oauth-scope', 'validate-oauth-token']
  },
  AuthController: {
    callback: ['csrf']
  }
};

// api/policies/validate-oauth-token.js
module.exports = function(req, res, next) {
  const authHeader = req.headers.authorization;
  if (!authHeader || !authHeader.startsWith('Bearer ')) {
    return res.unauthorized('OAuth2 token required');
  }
  const token = authHeader.split(' ')[1];
  // Call introspection endpoint or validate JWT
  validateToken(token).then(valid => {
    if (!valid) return res.unauthorized('Invalid token');
    next();
  }).catch(() => res.servererror('Token validation failed'));
};

By removing API key acceptance in OAuth-protected routes and enforcing scope validation, Sails applications reduce the risk of key exposure and ensure that OAuth 2.0 functions as intended. These practices align with findings that middleBrick would report under Authentication and BFLA checks, providing actionable remediation that developers can implement directly.

Frequently Asked Questions

Can an API key be safely used alongside OAuth 2.0 in Sails?
It is not recommended. Using both increases risk; OAuth 2.0 should be enforced without fallback to API keys. Remove key checks from OAuth-protected endpoints and validate tokens and scopes independently.
How does middleBrick detect API key exposure with OAuth 2.0?
middleBrick cross-references OpenAPI specs and runtime tests. If an endpoint accepts an API key without validating OAuth tokens, this discrepancy is flagged, highlighting improper credential handling.