You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rocketmq.apache.org by "unbridled-41 (via GitHub)" <gi...@apache.org> on 2026/09/08 13:23:03 UTC
[I] [Studio][Bug] Login reveals disabled accounts and skips rate limiting before the password is verified [rocketmq-dashboard]
unbridled-41 opened a new issue, #4159:
URL: https://github.com/apache/rocketmq-dashboard/issues/4159
## Problem
`AuthService.loginDatabaseUser` checks whether the Studio user is enabled **before** comparing the password:
```java
RmqStudioUser user = findUserByUsername(request.getUsername())
.orElseThrow(() -> new BusinessException(401, "Invalid username or password"));
if (!Boolean.TRUE.equals(user.getEnabled())) {
throw new BusinessException(403, "User account is disabled"); // before password check
}
if (!passwordHasher.matches(request.getPassword(), user.getPasswordHash())) {
throw new BusinessException(401, "Invalid username or password");
}
```
`AuthService.java` (lines 352-359 on the current `rocketmq-studio` head).
## Evidence
Two new regression tests in `AuthServiceDatabaseTest` fail on the unmodified base branch (`0a596661`):
- `loginShouldNotRevealDisabledAccountsBeforeThePasswordIsVerified` — a disabled user with a **wrong** password gets `403 "User account is disabled"` instead of the generic `401 "Invalid username or password"`. Actual red output: `Expecting message to be: "Invalid username or password" but was: "User account is disabled"`.
- `disabledAccountLoginsAreRateLimitedLikeWrongPasswords` — `LoginRateLimiter.recordFailure` is only called for `401` (`AuthService.login`, lines 138-143), so five failed attempts against a disabled account never reach `LoginRateLimiter.MAX_FAILED_ATTEMPTS` and no `429` lockout is applied. Actual red output: expected `429 Too many failed login attempts...` but got the sixth `403 "User account is disabled"`.
## Impact
- **User enumeration**: any unauthenticated caller can distinguish suspended usernames from unknown ones (`403` vs `401`) without knowing the password.
- **Brute-force bypass**: unlimited password guessing against disabled accounts — the failure counter is never incremented, so the existing `LoginRateLimiter` lockout never triggers for these attempts.
## Expected behavior
The disabled-account state must not be disclosed before the credentials are verified. The password should be compared first; a disabled account with a wrong password receives the same generic `401`. After a correct password, the existing `403 "User account is disabled"` response is preserved. Failed attempts against disabled accounts then flow through the existing login rate limiter like any other failed login.
## Related work
- #3045 / PR #3046 fixed the rate limiter's own capacity behavior (lockouts cleared by repeated failures / overflow bypass) — a different defect; the bypass here comes from which HTTP codes `AuthService.login` records, not from the limiter itself.
- PR #2876 (open) trims usernames and tolerates missing payloads — different defect.
- No open or closed issue covers the disabled-before-password ordering.
## PR
Fix incoming.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@rocketmq.apache.org.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [I] [Studio][Bug] Login reveals disabled accounts and skips rate limiting before the password is verified [rocketmq-dashboard]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] commented on issue #4159:
URL: https://github.com/apache/rocketmq-dashboard/issues/4159#issuecomment-6103715526
This issue has been automatically closed because there has been no activity for 7 days. If you believe this issue is still relevant, please reopen it with updated information.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@rocketmq.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [I] [Studio][Bug] Login reveals disabled accounts and skips rate limiting before the password is verified [rocketmq-dashboard]
Posted by "github-actions[bot] (via GitHub)" <gi...@apache.org>.
github-actions[bot] closed issue #4159: [Studio][Bug] Login reveals disabled accounts and skips rate limiting before the password is verified
URL: https://github.com/apache/rocketmq-dashboard/issues/4159
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@rocketmq.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org
Re: [I] [Studio][Bug] Login reveals disabled accounts and skips rate limiting before the password is verified [rocketmq-dashboard]
Posted by "RockteMQ-AI (via GitHub)" <gi...@apache.org>.
RockteMQ-AI commented on issue #4159:
URL: https://github.com/apache/rocketmq-dashboard/issues/4159#issuecomment-5586407710
## Bot Evaluation
**Classification:** Bug — Security (HIGH)
**Verification:** ✅ Confirmed via code analysis
### Analysis
Verified against `server/src/main/java/org/apache/rocketmq/studio/auth/AuthService.java`, method `loginDatabaseUser()` (lines ~264-272):
```java
RmqStudioUser user = findUserByUsername(request.getUsername())
.orElseThrow(() -> new BusinessException(401, "Invalid username or password"));
if (!Boolean.TRUE.equals(user.getEnabled())) {
throw new BusinessException(403, "User account is disabled"); // before password check
}
if (!passwordHasher.matches(request.getPassword(), user.getPasswordHash())) {
throw new BusinessException(401, "Invalid username or password");
}
```
The `enabled` check (403) executes before the password comparison (401), creating two security issues:
### Security Impact
1. **Account enumeration:** An attacker can distinguish between non-existent users (401), disabled accounts (403), and valid active accounts (401 after password mismatch). The distinct 403 response for disabled accounts reveals their existence.
2. **Rate-limit bypass:** If rate limiting is tied to failed password attempts, disabled accounts never reach the password check, so brute-force attempts against disabled accounts bypass rate limiting entirely.
### Recommended Fix
Check the password **first**, then check the enabled status. This ensures:
- Invalid credentials always return 401 regardless of account state (no information leakage).
- Rate limiting applies uniformly to all login attempts.
```java
// Correct order:
if (!passwordHasher.matches(request.getPassword(), user.getPasswordHash())) {
throw new BusinessException(401, "Invalid username or password");
}
if (!Boolean.TRUE.equals(user.getEnabled())) {
throw new BusinessException(401, "Invalid username or password"); // same message
}
```
The included regression tests (`loginShouldNotRevealDisabledAccountsBeforeThePasswordIsVerified`) correctly validate the expected behavior.
**Verdict:** Confirmed security bug. Fix PR recommended.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: commits-unsubscribe@rocketmq.apache.org
For queries about this service, please contact Infrastructure at:
users@infra.apache.org