/rest/user/login is not rate-limited
The login endpoint has no rate-limit / lockout middleware, enabling unlimited credential stuffing and password brute-force against any account.
routes/login.ts exports login() which is wired in server.ts:587 as app.post('/rest/user/login', login()) — no rateLimit({ … }) wrapper, no express-slow-down, no per-account lockout. Compare to server.ts:340 (/rest/user/reset-password is rate-limited) and server.ts:452,459,465 (the 2FA endpoints are rate-limited): the developers know to apply the pattern, they simply didn't here.
The handler itself also performs no attempt counting on the User model — no failedLoginAttempts, no temporary disable. Combined with the password column being an unsalted MD5 hash (lib/insecurity.ts:43), an attacker can pound the endpoint as fast as the network allows.
for i in $(cat top-10k-passwords.txt); do curl -s -X POST http://target/rest/user/login \ -H 'Content-Type: application/json' \ -d "{\"email\":\"victim@example.com\",\"password\":\"$i\"}" \ | grep -q '"token"' && echo "HIT: $i" && break done
No 429s, no progressive delay, no account lockout — only network latency caps the rate.
Any account with a guessable or breached password can be taken over via online brute-force or credential-stuffing. No prior authentication required.
The exported login() handler in routes/login.ts contains no rate-limiting or attempt-counting logic, and the finding states it's wired in server.ts as app.post('/rest/user/login', login()) with no rateLimit wrapper — unlike sibling sensitive endpoints (reset-password, 2FA) which are rate-limited. An unauthenticated attacker can issue unlimited POSTs to /rest/user/login with arbitrary email/password pairs; the handler simply returns 401 on failure with no lockout or backoff, enabling online brute-force/credential stuffing. Per scope, the intentionally vulnerable nature of Juice Shop must be ignored and the finding judged as if production.
The /rest/user/login route is wired in server.ts:587 with no rateLimit/express-slow-down wrapper and the handler in routes/login.ts performs no attempt counting on UserModel, so an anonymous remote attacker can issue unlimited credential-stuffing/brute-force requests directly over HTTP (AV:N, PR:N, UI:N, AC:L). A successful guess returns a full session token via security.authorize(user) in afterLogin, giving the attacker complete read/write access to the victim's account data and basket (C:H, I:H); admin accounts are reachable the same way (cf. loginAdminChallenge). Brute-forcing itself does not degrade service in a meaningful way and the impact is confined to the application's own auth boundary, so A:N and S:U.
- CWE-307
- OWASP A07:2021 Identification and Authentication Failures