HIGH credential stuffinghapi

Credential Stuffing in Hapi

How Credential Stuffing Manifests in Hapi

Credential stuffing attacks against Hapi applications typically target authentication endpoints, such as /login or /session, where the framework processes credentials. Hapi's modular architecture means authentication logic is often implemented via plugins like hapi-auth-basic or hapi-auth-cookie. A common vulnerability arises when these endpoints lack rate limiting or account lockout mechanisms, allowing attackers to automate login attempts using credential pairs harvested from previous data breaches.

In a typical Hapi route configuration, an unprotected login handler might look like this:

server.route({
  method: 'POST',
  path: '/login',
  options: {
    auth: 'simple', // Uses a basic auth strategy
    handler: async (request, h) => {
      const { username, password } = request.payload;
      // Validate credentials against database
      const user = await User.findOne({ username });
      if (user && await bcrypt.compare(password, user.passwordHash)) {
        request.cookieAuth.set(user);
        return h.response({ status: 'authenticated' });
      }
      throw Boom.unauthorized('Invalid credentials');
    }
  }
});

Here, the auth: 'simple' strategy delegates to a validate function that runs on every request. Without additional constraints, an attacker can script thousands of requests with different password guesses for a single username, or cycle through known username/password pairs across many accounts. Hapi does not impose default limits on authentication attempts, making it the developer's responsibility to enforce throttling or lockout policies at the route or server level.

Another manifestation occurs when Hapi applications use JWT-based authentication without proper revocation checks. If a token is stolen via stuffing, it remains valid until expiration. Hapi's hapi-auth-jwt2 plugin, for example, will accept any validly signed token unless additional context (like a token blacklist) is manually implemented.

Hapi-Specific Detection

Detecting credential stuffing vulnerabilities in a Hapi API requires testing authentication endpoints for both weak validation and missing rate limiting. middleBrick performs black-box scanning by sending sequential login attempts with common passwords (e.g., 123456, password) and observing responses. A lack of rate limiting is indicated by consistent HTTP 200/401 status codes without Retry-After headers or incremental delays. middleBrick's Authentication and Rate Limiting checks flag these issues, mapping them to OWASP API Top 10: API2:2023 — Broken Authentication.

For example, middleBrick might produce this finding in its report:

CheckSeverityEndpointEvidence
Rate LimitingHighPOST /loginNo X-RateLimit-* headers; 50 requests succeeded within 10 seconds
AuthenticationCriticalPOST /loginAccepted weak password 123456 for user admin

To scan a Hapi API yourself, use the middleBrick CLI:

middlebrick scan https://your-hapi-api.com/login

The output includes a per-category breakdown and prioritized remediation guidance. The scan takes 5–15 seconds and requires no credentials, as it only tests the unauthenticated attack surface.

Hapi-Specific Remediation

Remediating credential stuffing in Hapi involves implementing rate limiting, enforcing strong passwords, and adding account lockout mechanisms. Hapi does not include built-in rate limiting, but the widely used hapi-rate-limit plugin integrates seamlessly. Configure it at the server level to throttle all routes, or apply selectively to authentication endpoints.

1. Install and register hapi-rate-limit:

const HapiRateLimit = require('hapi-rate-limit');

await server.register({
  plugin: HapiRateLimit,
  options: {
    rateLimit: {
      max: 5, // 5 requests per minute
      timeWindow: 'minute',
      headers: true // Adds X-RateLimit-* headers
    },
    // Apply only to /login to avoid caching issues
    routes: {
      path: '/login',
      method: 'POST'
    }
  }
});

2. Implement account lockout in your auth strategy: Track failed attempts per username (e.g., in Redis) and reject logins after a threshold. Modify your validate function:

const failedAttempts = new Map(); // Replace with persistent store

const validate = async (request, username, password, h) => {
  const attempts = failedAttempts.get(username) || 0;
  if (attempts >= 5) {
    return { credentials: null, artifacts: { error: 'Account locked' } };
  }
  const user = await User.findOne({ username });
  if (user && await bcrypt.compare(password, user.passwordHash)) {
    failedAttempts.set(username, 0); // Reset on success
    return { credentials: user };
  }
  failedAttempts.set(username, attempts + 1);
  return { credentials: null, artifacts: { error: 'Invalid credentials' } };
};

server.auth.strategy('simple', 'basic', { validate });

3. Enforce password complexity during registration: Use joi validation to reject weak passwords.

const schema = Joi.object({
  username: Joi.string().required(),
  password: Joi.string()
    .pattern(new RegExp('^(?=.*[a-z])(?=.*[A-Z])(?=.*\d).{12,}$'))
    .required()
    .messages({
      'string.pattern.base': 'Password must be 12+ chars with uppercase, lowercase, and number'
    })
});

After applying these fixes, re-scan with middleBrick to verify the Rate Limiting and Authentication checks pass. The Pro plan's continuous monitoring can alert you if rate limits are accidentally removed in future deployments.

Frequently Asked Questions

Does middleBrick require credentials to scan my Hapi API?
No. middleBrick performs unauthenticated black-box scanning only. You simply provide the endpoint URL—no usernames, passwords, or API keys are needed. This ensures we test the same attack surface an external attacker would see.
How often should I scan my Hapi APIs with middleBrick?
For production APIs, we recommend continuous monitoring (available on Pro and Enterprise plans) to scan on a schedule (e.g., daily) and after every deployment via the GitHub Action. The Free tier allows periodic manual scans, while Starter includes monthly scans. Critical endpoints may warrant more frequent checks.