Skip to content

Security/vulnerability hardening#93

Merged
HamzaAburaneh merged 5 commits into
mainfrom
security/vulnerability-hardening
Jun 25, 2026
Merged

Security/vulnerability hardening#93
HamzaAburaneh merged 5 commits into
mainfrom
security/vulnerability-hardening

Conversation

@HamzaAburaneh

Copy link
Copy Markdown
Contributor

Security/vulnerability hardening

Fixes 8 security vulnerabilities identified in an internal audit. All changes include regression tests.


Critical

  • Unauthenticated admin takeover/enableAdmin was publicly accessible with no authentication. Any user could promote any account to admin with a single GET request. Removed from permitAll(), added @PreAuthorize("hasAuthority('ADMIN')") at class level on UserController, enabled @EnableMethodSecurity.

High

  • IP spoofing in GcIpFilter — The GC IP allow-list filter read X-Forwarded-For first, which is fully attacker-controlled. Replaced with a configurable strategy (gc.ip.filter.client-ip-source) that trusts only the correct header per deployment (nginx: X-Real-IP, AWS ALB: rightmost X-Forwarded-For, direct: REMOTE_ADDR).
  • XSS in admin user listing — User-supplied fields (email, institution, roles) were interpolated directly into HTML and rendered unsanitized. Fixed with HtmlUtils.htmlEscape() on all user-supplied values.

Medium

  • CSRF globally disabled — Re-enabled with a targeted exemption for the JWT /authenticate endpoint only. Admin AJAX calls updated from GET to POST with CSRF token injection via $.ajaxSetup.
  • State-changing GET endpoints/u/delete and /u/update were HTTP GET. Changed to POST; templates updated with CSRF token header.
  • API user credentials in query string/createApiUser was a GET endpoint, leaking username and password into server logs and browser history. Changed to POST with JSON request body.

Low

  • Open redirect after login — Post-login redirect followed any saved URL without same-origin validation. Added isSameOrigin() check; external URLs fall back to role-based default redirect.
  • Weak JWT secret defaultjwt.secret=javainuse (well-known tutorial default) removed from config. JWTUtil now throws IllegalStateException at startup if the signing key is under 32 characters.

Tests

19 regression tests, all passing. New test classes: UserControllerXssTest, AuthControllerTest, CustomizeAuthenticationSuccessHandlerTest, UserControllerSecurityTest, GcIpFilterTest.

@HamzaAburaneh HamzaAburaneh self-assigned this Jun 25, 2026
@HamzaAburaneh HamzaAburaneh requested a review from a team June 25, 2026 18:32
@HamzaAburaneh HamzaAburaneh merged commit 2bcbfb5 into main Jun 25, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants