fix UI privilege escalation vulnerability

- Add requireAdminRole middleware to web UI that checks
  claims.HasRole("admin") and returns 403 if absent
- Apply middleware to all admin routes (accounts, policies,
  audit, dashboard, credentials)
- Remove redundant inline admin check from handleAdminResetPassword
- Profile routes correctly require only authentication, not admin

Security: The admin/adminGet middleware wrappers only called
requireCookieAuth (JWT validation) but never verified the admin
role. Any authenticated user could access admin endpoints
including role assignment. Fixed by inserting requireAdminRole
into the middleware chain for all admin routes.
This commit is contained in:
2026-03-12 21:59:02 -07:00
parent d9c904b0f4
commit 8d9d9da6f5
2 changed files with 31 additions and 25 deletions

View File

@@ -914,26 +914,8 @@ func (u *UIServer) handleCreatePGCreds(w http.ResponseWriter, r *http.Request) {
// storage. The plaintext is never logged or included in any response.
// Audit event EventPasswordChanged is recorded on success.
func (u *UIServer) handleAdminResetPassword(w http.ResponseWriter, r *http.Request) {
// Security: enforce admin role; requireCookieAuth only validates the token,
// it does not check roles. A non-admin with a valid session must not be
// able to reset arbitrary accounts' passwords.
callerClaims := claimsFromContext(r.Context())
if callerClaims == nil {
u.renderError(w, r, http.StatusUnauthorized, "unauthorized")
return
}
isAdmin := false
for _, role := range callerClaims.Roles {
if role == "admin" {
isAdmin = true
break
}
}
if !isAdmin {
u.renderError(w, r, http.StatusForbidden, "admin role required")
return
}
// Security: admin role is enforced by the requireAdminRole middleware in
// the route registration (ui.go); no inline check needed here.
r.Body = http.MaxBytesReader(w, r.Body, maxFormBytes)
if err := r.ParseForm(); err != nil {
u.renderError(w, r, http.StatusBadRequest, "invalid form")