HIGH credential stuffinghapijwt tokens

Credential Stuffing in Hapi with Jwt Tokens

Credential Stuffing in Hapi with Jwt Tokens — how this specific combination creates or exposes the vulnerability

Credential stuffing is an automated attack in which lists of breached username and password pairs are systematically submitted to an endpoint to take over accounts. When using JWT tokens in a Hapi application, the vulnerability surface depends on how tokens are issued, validated, and accepted. A common insecure pattern is accepting a JWT access token for authentication while still allowing or weakly validating the original credential context, such as username or password reuse across services. For example, if a Hapi server exposes a login route that returns a JWT on successful primary authentication but does not enforce rate limits or step-up checks on subsequent token-use paths, an attacker can replay stolen credentials to obtain valid tokens and access protected resources.

Additionally, if JWTs are accepted via insecure channels (e.g., unauthenticated endpoints that reflect or accept tokens in query parameters or headers without strict origin checks), credential stuffing can be coupled with token leakage. Consider a scenario where a compromised frontend or API client leaks JWTs; attackers can pair leaked tokens with credential lists to test whether associated accounts remain active or whether token binding is weak. Hapi applications that implement permissive CORS, accept unsigned or weakly signed tokens, or do not require re-authentication for sensitive actions increase the risk. The combination of weak account lockout, missing multi-factor authentication, and JWTs accepted without additional context checks allows attackers to iterate credentials while using stolen tokens to bypass repeated password-based failures.

Another specific risk involves stateless JWT validation without server-side awareness of credential compromise. If a Hapi service validates JWT signatures but does not check a revocation mechanism (such as a short-lived token strategy, token blocklist, or binding to device fingerprints), attackers can continue using tokens obtained via credential stuffing until expiration. Inadequate audience and issuer validation in Hapi JWT verification can also lead to accepting tokens intended for other services, enabling cross-service credential reuse. For instance, accepting tokens with overly broad scopes or without checking nonce values can allow an attacker to reuse compromised credentials across integrated systems while presenting a valid JWT for each successive attempt.

Jwt Tokens-Specific Remediation in Hapi — concrete code fixes

To reduce risk, enforce strict JWT validation and contextual checks in Hapi routes. Below are concrete, working examples that demonstrate secure handling of JWTs alongside credential checks.

Secure login with rate limiting and token binding

const Hapi = require('@hapi/hapi');
const jwt = require('jsonwebtoken');
const rateLimit = require('hapi-rate-limit');

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

  // Rate limit login to mitigate credential stuffing
  server.ext('onPreAuth', rateLimit({
    rateLimit: {
      max: 5,
      timeWindow: 60 // 5 attempts per minute per IP
    },
    configKey: 'login-rate-limit'
  }));

  server.route({
    method: 'POST',
    path: '/login',
    handler: (request, h) => {
      const { username, password } = request.payload;
      // Validate credentials against a secure store (e.g., bcrypt compare)
      const isValid = validateUser(username, password); // implement secure compare
      if (!isValid) {
        return { error: 'Invalid credentials' };
      }
      // Bind token to client context: include a per-user salt or device identifier
      const token = jwt.sign(
        {
          sub: username,
          scope: 'read write',
          jti: require('crypto').randomUUID(), // token identifier for revocation checks
          deviceId: request.headers['x-device-id'] || 'unknown'
        },
        process.env.JWT_SECRET,
        { algorithm: 'HS256', expiresIn: '15m', issuer: 'hapi-auth', audience: 'hapi-api' }
      );
      return h.response({ token }).code(200);
    }
  });

  server.route({
    method: 'GET',
    path: '/profile',
    options: {
      // Validate JWT on protected routes
      auth: {
        strategy: 'jwt',
        mode: 'required'
      }
    },
    handler: (request, h) => {
      // request.auth.credentials is validated by Hapi JWT strategy
      const { sub, jti, deviceId } = request.auth.credentials;
      // Additional checks: verify token usage context (e.g., deviceId matches)
      if (deviceId !== request.headers['x-device-id']) {
        throw Boom.unauthorized('Token context mismatch');
      }
      // Check revocation (pseudo function)
      if (isTokenRevoked(jti)) {
        throw Boom.unauthorized('Token revoked');
      }
      return { user: sub, status: 'active' };
    }
  });

  await server.start();
};

// JWT validation strategy example using hapi-auth-jwt2
const JwtStrategy = require('hapi-auth-jwt2');
server.register(JwtStrategy, (err) => {
  if (err) throw err;
  server.auth.strategy('jwt', 'jwt', {
    key: process.env.JWT_SECRET,
    validate: async (decoded, request) => {
      // Enforce issuer/audience checks
      if (decoded.iss !== 'hapi-auth' || decoded.aud !== 'hapi-api') {
        return { isValid: false };
      }
      // Enforce short expiry and revocation
      const isValid = !isTokenRevoked(decoded.jti) && decoded.exp > Math.floor(Date.now() / 1000);
      return { isValid, credentials: decoded };
    },
    verifyOptions: {
      algorithms: ['HS256'],
      maxAge: '2m' // enforce client-side refresh to reduce replay window
    }
  });
});

Additional hardening measures

  • Always hash passwords with a strong adaptive function (e.g., bcrypt) before comparing.
  • Implement multi-factor authentication for high-risk actions and for accounts identified in credential stuffing lists.
  • Use short JWT lifetimes and refresh tokens with strict rotation and revocation.
  • Validate JWT claims (iss, aud, exp, nbf) consistently and reject unsigned tokens.
  • Bind tokens to client context (IP/device fingerprint) when feasible and re-validate on sensitive operations.

These examples show how Hapi can combine JWT best practices with credential-aware controls to reduce the impact of credential stuffing. Security requires both strong token handling and ongoing monitoring of authentication patterns.

Frequently Asked Questions

How does middleBrick help detect JWT-related risks in Hapi APIs?
middleBrick scans unauthenticated attack surfaces and includes checks for authentication weaknesses and unsafe consumption patterns; findings include guidance on JWT validation, revocation, and token binding.
Can middleBrick test for LLM security issues involving JWT handling in prompts?
Yes; the LLM/AI Security checks include active prompt injection testing and system prompt leakage detection, which can surface risks where JWT handling or authentication logic might be exposed or manipulated via crafted inputs.