fter analyzing hundreds of Spring Boot projects, these are the security misconfigurations that appear again and again — and how to fix them in 5 minutes.
JOptimize Team
After running JOptimize on hundreds of Spring Boot projects, a pattern emerges. The same security misconfigurations appear again and again — not because developers don't care, but because they're easy to miss in code review and don't cause obvious runtime errors.
Here are the 5 most common ones, ranked by how often we see them.
// This is in almost every project that started from a tutorial http.csrf(csrf -> csrf.disable());
Most tutorials disable CSRF to make REST API testing easier. The problem is developers copy this into production without thinking about it.
When it's actually OK: Fully stateless APIs using JWT or API keys — no session cookies involved.
When it's dangerous: Any app with session-based auth, remember-me tokens, or a mix of REST and form-based login.
The fix:
// For stateless JWT API — OK, but document it http.csrf(csrf -> csrf.disable()); // Stateless JWT auth, no session // For stateful app — use proper CSRF tokens http.csrf(csrf -> csrf .csrfTokenRepository(CookieCsrfTokenRepository.withHttpOnlyFalse()) );
// Seen in production more times than I can count config.addAllowedOrigin("*");
This one is particularly common because it "works" during development and nobody revisits it before launch.
The fix is two lines:
config.setAllowedOrigins(List.of( "https://yourdomain.com", "https://www.yourdomain.com" ));
Remove http://localhost:3000 before going to production.
// Added to "make it work" during development, never removed .authorizeHttpRequests(auth -> auth .anyRequest().permitAll() );
This is the most dangerous one on this list. Every endpoint — including admin routes, payment endpoints, user data — is publicly accessible with no authentication.
The fix:
.authorizeHttpRequests(auth -> auth .requestMatchers("/api/public/**").permitAll() .requestMatchers("/api/admin/**").hasRole("ADMIN") .anyRequest().authenticated() // ← default to authenticated );
Default to authenticated. Whitelist only what needs to be public.
@DeleteMapping("/users/{id}") public void deleteUser(@PathVariable Long id) { // Any authenticated user can delete any account userRepository.deleteById(id); }
Even with .anyRequest().authenticated(), any logged-in user can call any endpoint. Role-based access needs to be explicit.
The fix:
@DeleteMapping("/users/{id}") @PreAuthorize("hasRole('ADMIN') or #id == authentication.principal.id") public void deleteUser(@PathVariable Long id) { userRepository.deleteById(id); }
Don't forget @EnableMethodSecurity on your security config.
# application.properties — these are often set to "always" during dev server.error.include-stacktrace=always server.error.include-message=always
Stack traces in API responses give attackers a detailed map of your application — package names, class names, library versions, exact line numbers.
The fix:
server.error.include-stacktrace=never server.error.include-message=never server.error.include-binding-errors=never
And add a @RestControllerAdvice to return generic error messages.
Running JOptimize on a Spring Boot project flags all of these automatically:
joptimize analyze . # → [CRITICAL] CSRF disabled — SecurityConfig.java:24 # → [CRITICAL] CORS wildcard detected — SecurityConfig.java:51 # → [CRITICAL] anyRequest().permitAll() — SecurityConfig.java:35 # → [CRITICAL] Write endpoint without @PreAuthorize — UserController.java:45
Each issue links to the exact file and line, with a suggested fix.
npm install -g @joptimize/cli joptimize auth jp_live_your_key joptimize analyze .
Or upload your ZIP at joptimize.io — results in 30 seconds.
Master Spring Boot, security, and Java performance with hands-on courses.
JOptimize finds N+1 queries, EAGER collections, and 70+ other issues in your Java codebase — in under 30 seconds.