HIGH broken authenticationhapiapi keys

Broken Authentication in Hapi with Api Keys

Broken Authentication in Hapi with Api Keys — how this specific combination creates or exposes the vulnerability

Hapi is a rich framework for building HTTP services in Node.js. When using API keys for authentication, common implementation choices can unintentionally create authentication bypasses or exposure. A typical vulnerability pattern in Hapi arises when keys are passed in URLs, in non-secure cookies, or without sufficient transport and storage protections, and when route validation does not consistently enforce key presence and scope.

Consider an endpoint that retrieves user profile data. If the route is defined without requiring authentication, or if a developer accidentally omits the auth configuration for a subset of routes, unauthenticated access becomes possible. Even when auth is declared, using a query parameter such as api_key without enforcing HTTPS can expose the key in server logs, browser history, and network appliances. Hapi’s route options must explicitly apply an authentication strategy to every route that handles sensitive operations; missing enforcement effectively disables authentication for those paths.

Another specific risk involves how API keys are validated. Custom authentication handlers in Hapi should perform constant-time comparisons to avoid timing attacks. A naive string equality check can allow an attacker to learn partial information about a valid key through response-time differences. Additionally, failing to bind keys to a scope, such as a specific set of routes or a tenant identifier, can enable privilege escalation when a key intended for read-only usage is accepted by write endpoints.

Transport vulnerabilities compound the issue. If keys are transmitted over HTTP, an on-path attacker can intercept them and reuse them indefinitely. Hapi can be configured to require HTTPS via a proxy or by using a TLS termination layer, but if the application does not reject non-TLS requests or validate the Secure flag on cookies, the key material may leak. Similarly, storing keys in insecure locations, such as world-readable configuration files or logs, increases the likelihood of unauthorized discovery.

Operational misconfigurations also contribute to broken authentication. For example, failing to rotate keys after staff changes, not revoking compromised keys, or using the same key across multiple environments can create lateral movement paths. In a microservices context, if service A passes a key to service B without verifying the recipient, an attacker who compromises service B may be able to pivot. These patterns illustrate how authentication in Hapi with API keys can become weak when security controls are incomplete or inconsistently applied across the API surface, as detected by security scans that test unauthenticated attack surfaces and BOLA/IDOR conditions.

Api Keys-Specific Remediation in Hapi — concrete code fixes

To remediate broken authentication when using API keys in Hapi, enforce authentication on all sensitive routes, use secure transmission and storage practices, and validate keys safely. Below are concrete, working examples that you can adapt to your service.

1. Require authentication on all routes that handle sensitive operations

Define a dedicated authentication strategy and apply it explicitly to each route. This prevents accidental omission of protection.

const Hapi = require('@hapi/hapi');
const ApiKeyValidator = async (request, h) => {
  const key = request.headers['x-api-key'];
  const validKeys = new Set([process.env.API_KEY_PROD, process.env.API_KEY_STAGING]);
  if (!key || !validKeys.has(key)) {
    return h.authenticated({ credentials: { key } }).fail({
      statusCode: 401,
      message: 'Invalid or missing API key'
    });
  }
  return h.authenticated({ credentials: { key } });
};

const server = Hapi.server({ port: 4000, host: 'localhost' });

server.auth.scheme('apikey', () => ({
  authenticate: ApiKeyValidator
}));

server.auth.strategy('simpleApiKey', 'apikey');

server.route([
  {
    method: 'GET',
    path: '/profile',
    config: {
      auth: 'simpleApiKey',
      handler: (request, h) => {
        return { message: 'Profile data for authenticated key' };
      }
    }
  },
  {
    method: 'POST',
    path: '/admin/reset',
    config: {
      auth: 'simpleApiKey',
      handler: (request, h) => {
        return { message: 'Admin action executed' };
      }
    }
  }
]);

const start = async () => {
  await server.start();
  console.log('Server running on %s', server.info.uri);
};

start().catch(err => {
  console.error(err);
  process.exit(1);
});

2. Transmit keys only over HTTPS and reject insecure requests

Ensure the server rejects non-TLS requests and that keys are transmitted only over encrypted channels. In environments with a reverse proxy or load balancer, validate headers carefully and do not rely on HTTP for key transmission.

// Enforce secure connections at the server level where possible
const server = Hapi.server({
  port: 443,
  host: '0.0.0.0',
  tls: {
    key: fs.readFileSync('/path/to/private.key'),
    cert: fs.readFileSync('/path/to/certificate.crt')
  }
});

// Reject requests that do not use secure headers in fronted setups
server.ext('onRequest', (request, h) => {
  const forwardedProto = request.headers['x-forwarded-proto'];
  if (forwardedProto && forwardedProto !== 'https') {
    return h.response({ error: 'HTTPS required' }).code(403);
  }
  return h.continue;
});

3. Use constant-time comparison and avoid logging keys

Prevent timing attacks by comparing keys in constant time and ensure keys are not written to logs. Do not concatenate keys into URLs; use headers instead.

const crypto = require('crypto');

const safeKeyCompare = (candidate, expected) => {
  return crypto.timingSafeEqual(Buffer.from(candidate), Buffer.from(expected));
};

// Usage inside validator
const candidate = request.headers['x-api-key'];
if (!candidate || !safeKeyCompare(candidate, expectedBuffer)) {
  return h.authenticated().fail({ statusCode: 401, message: 'Invalid key' });
}

4. Scope keys to specific routes or tenants and rotate regularly

Bind keys to intended usage and rotate them on a schedule. Avoid sharing keys across services without verification.

const keyScopes = {
  'read-only-key': ['GET:/profile', 'GET:/status'],
  'admin-key': ['POST:/admin/reset', 'DELETE:/resource']
};

const validateScope = (key, method, path) => {
  const scope = Object.keys(keyScopes).find(k => keyScopes[k].includes(`${method}:${path}`));
  return scope ? { valid: true, scope } : { valid: false };
};

// Integrate into ApiKeyValidator to enforce scope checks

Related CWEs: authentication

CWE IDNameSeverity
CWE-287Improper Authentication CRITICAL
CWE-306Missing Authentication for Critical Function CRITICAL
CWE-307Brute Force HIGH
CWE-308Single-Factor Authentication MEDIUM
CWE-309Use of Password System for Primary Authentication MEDIUM
CWE-347Improper Verification of Cryptographic Signature HIGH
CWE-384Session Fixation HIGH
CWE-521Weak Password Requirements MEDIUM
CWE-613Insufficient Session Expiration MEDIUM
CWE-640Weak Password Recovery HIGH

Frequently Asked Questions

What should I do if a scan flags broken authentication in my Hapi API using API keys?
Treat the finding as high priority: ensure every sensitive route explicitly requires authentication, transmit keys only over HTTPS using headers rather than query parameters, and avoid logging key material. Rotate keys and validate scope to limit lateral movement.
Can middleBrick help detect API key related authentication issues in Hapi APIs?
middleBrick scans unauthenticated attack surfaces and includes checks such as BOLA/IDOR and authentication configuration analysis, which can highlight missing or inconsistent enforcement of API key authentication in Hapi services.