Q12 of 24 · Security

How do you test rate limiting and brute-force protection on an authentication endpoint?

SecurityMidsecurityrate-limitingbrute-forceauthenticationlockouttesting

Short answer

Short answer: Send a rapid sequence of login requests with invalid credentials and assert the application responds with 429 Too Many Requests, introduces an account lockout, or requires a CAPTCHA after the configured threshold. Verify that a valid login after the lockout window succeeds.

Detail

Brute-force protection comes in several forms — test each specifically:

Rate limiting (429 response): Send 20-30 requests in quick succession to the login endpoint with invalid credentials. Assert HTTP 429 is returned at or before the configured threshold. Check the Retry-After header — it should specify when the client can retry. Verify the rate limit resets correctly after the window expires.

Account lockout: Submit N failed login attempts for a specific username. Verify the account is locked (response changes to 403 with a "locked" message, or the next attempt fails even with correct credentials). Verify the lockout duration is reasonable (5-15 minutes) and that there is a legitimate unlock mechanism (email link, admin reset).

CAPTCHA / device verification: After N failures, some applications introduce a CAPTCHA challenge. Verify the challenge is enforced before the Nth+1 attempt — not after.

Edge cases to test:

  • Rate limiting by IP vs by account: an attacker distributing requests across IPs should still be rate-limited per account.
  • Timing attacks: does the response time differ noticeably between valid and invalid usernames? A significant timing difference can be used to enumerate valid accounts.
  • "Slow" brute force: does rate limiting apply only to rapid bursts, or also to slow password spraying across hours?

What not to do in testing: use a test account dedicated to security testing — never run brute-force tests against production accounts, as lockouts will affect real users.

// EXAMPLE

rate-limit.test.ts

test('login endpoint rate-limits after 10 failed attempts', async ({ request }) => {
  for (let i = 0; i < 10; i++) {
    await request.post('/api/login', {
      data: { email: 'test@example.com', password: 'wrong-password' },
    });
  }

  const response = await request.post('/api/login', {
    data: { email: 'test@example.com', password: 'wrong-password' },
  });

  expect(response.status()).toBe(429);
  expect(response.headers()['retry-after']).toBeTruthy();
});

// WHAT INTERVIEWERS LOOK FOR

Tests rate limiting, account lockout, and post-lockout valid login separately. Notes IP vs account scope. Mentions the test-account safety consideration.