Swagger UI exposed to unauthenticated users in all environments #6
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
/swagger-ui/**and/v3/api-docs/**are configured aspermitAll()inSecurityConfig, exposing the full API documentation to unauthenticated users in all environments including production.Affected files
SecurityConfig.java:28Attack scenario
An attacker accesses
/swagger-ui/index.htmland obtains a complete map of all endpoints, parameters, request/response schemas, and DTOs — significantly reducing reconnaissance effort.Recommended fix
Either:
dev/testprofiles, orROLE_ADMINauthentication to access Swagger in production.Severity
High — full API surface disclosed to unauthenticated users.
👨💻 Kai — Frontend Engineer
Pure backend/security config change — no frontend code involved. But a couple of things worth thinking about from my side:
Developer experience during local development:
dev/testonly), it won't be available in production, which is the goal. But we should confirm our local dev setup (application-dev.ymlor Docker Compose) always activates thedevprofile so the team can still use Swagger locally without extra steps.ROLE_ADMINauth in all environments, developers will need to log in as an admin user before accessing Swagger — make sure the local seed data includes an admin user for this purpose.API documentation alternatives:
openapi.yaml) so we can reference it without needing a running instance?openapi.yamluseful — it means I can validate request/response shapes during component development without hitting the backend.Questions:
ROLE_ADMINrequired? Both have tradeoffs for the dev workflow.ROLE_ADMINapproach creates friction for the whole team.🔧 Backend Engineer — Spring Boot / PostgreSQL Specialist
Good catch —
permitAll()on Swagger in production is a common oversight. The two proposed fixes have different tradeoffs, and I'd recommend option 1 (profile-gating) as the primary approach, with option 2 (admin auth) as a complement if we ever need Swagger accessible in production at all.Preferred implementation — profile-gating:
A cleaner approach: configure
springdoc-openapito only activate indevandtestprofiles usingspringdoc.api-docs.enabled=falseinapplication-prod.yml. This disables the OpenAPI endpoints entirely in production — no security rule needed because the paths don't exist.SecurityConfig.javacleanup:springdoc.api-docs.enabled=false, remove thepermitAll()rule for/swagger-ui/**and/v3/api-docs/**fromSecurityConfigentirely in the production config. A dead security exception that permits a non-existent path is confusing and a maintenance liability.If
ROLE_ADMINapproach is chosen instead:withCredentials: truein the Swagger config) — otherwise the API calls from the UI will be unauthenticated even if the user is logged in.OpenAPI spec export:
mvn springdoc-openapi:generate(or equivalent) to exportopenapi.yamlas a build artifact. This gives the frontend team the contract without needing a running server.Questions:
springdoc-openapicurrently configured anywhere (e.g.,SpringDocConfig.java)? If so, profile-gating at the bean level is the cleanest approach.prodexplicitly set, or is it running on the default profile?🧪 QA Engineer
Small security config change with targeted test needs. Here's the coverage I'd want:
Integration tests — profile-gating approach:
@ActiveProfiles("prod"): GET/swagger-ui/index.html→ 404 (endpoint doesn't exist)@ActiveProfiles("prod"): GET/v3/api-docs→ 404@ActiveProfiles("dev"): GET/swagger-ui/index.html→ 200 (unauthenticated, dev is fine)@ActiveProfiles("dev"): GET/v3/api-docs→ 200Integration tests — ROLE_ADMIN approach (if chosen instead):
/swagger-ui/index.html→ 401 or 302 redirect to loginROLE_USER→ 403ROLE_ADMIN→ 200/v3/api-docs→ 401Regression — existing SecurityConfig tests:
permitAll()rules (login, register, public endpoints) still work as expected after the changeSecurityFilterChain— order of rules matters in Spring SecurityManual verification before closing:
Edge cases:
/swagger-ui.html(the old redirect path that springdoc sometimes still serves)? Should that also be gated?/webjars/**(used by Swagger UI for its assets) need to be addressed too?Questions:
@ActiveProfiles("prod")test slices)?🔐 Sable — Security Engineer
This is OWASP A05 (Security Misconfiguration) and correctly labeled high. An exposed Swagger UI in production is a gift to an attacker — it's the difference between hours of manual API discovery and having a complete, structured map handed to them in minutes.
Risk is broader than the issue describes:
Recommended approach — defense in depth:
springdoc.api-docs.enabled=falseandspringdoc.swagger-ui.enabled=falseinapplication-prod.yml. This removes the paths entirely — no security rule needed because there's nothing to protect.permitAll()rule for these paths fromSecurityConfigwhen running with the prod profile. A permissive rule for a disabled endpoint is dead code that misleads future reviewers.SecurityConfig— if springdoc is still active and only the security rule changes, a misconfiguration (e.g., wrong profile active) re-exposes everything. Belt and suspenders.Adjacent paths to audit:
/v3/api-docs/**(OpenAPI JSON) — directly machine-readable, arguably more dangerous than the UI/swagger-ui.html— redirect path that some springdoc versions still serve/webjars/**— Swagger UI static assets; if these arepermitAll()they also leak the springdoc version (useful for CVE targeting)/actuator/**— if exposed, as dangerous as Swagger. Worth auditing in the same pass.Verify the production profile is actually active:
SPRING_PROFILES_ACTIVE=prodenvironment variable, it runs on the default profile — and the fix may not apply. Confirm the deployment configuration (Docker Compose, systemd unit, etc.) explicitly sets the active profile.Questions:
SPRING_PROFILES_ACTIVEexplicitly set in the production deployment configuration, or is the app running on the default profile?/swagger-ui/index.htmlfrom outside the network)?🎨 Atlas — UI/UX Designer
This is a pure backend security configuration issue — no design work required, and I have no concerns about user-facing impact. The Swagger UI is a developer tool, not a user-facing screen, so gating or removing it has zero effect on any of our 18 app screens.
That said, a couple of observations that touch the broader product experience:
Internal tooling vs. end-user trust:
/swagger-ui/index.html, they see a detailed technical map of our internal API. That's not the impression we want to give, even if it's not directly exploitable in their hands.If we ever need a public-facing API docs surface:
No action items for me on this issue. Happy to design an admin-facing API explorer screen in a future v2 scope if there's ever a product need for it.
Questions:
ROLE_ADMINapproach.