Spring security
47 Claude skills for security tradecraft across AppSec, Pentest, Blue Team, and GRC. NL/EU regulatory-anchored. Pattern-level discipline.
npx -y skills add roodlicht/accans-sec-skills --skill spring-securityAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
2 things to look at
- no licenseNo license file was found in the repository. Code published without one is not open source by default, so using it at work is a question for whoever answers licensing questions where you are.
- 4 stars4 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Spring Boot security review — Spring Security config (SecurityFilterChain), OAuth2/OIDC client and resource-server, method-level @PreAuthorize, JWT validation, actuator endpoint lockdown, CSRF model for web vs API, and recent Spring CVE patterns (Spring4Shell, SpEL injection, authorization bypasses).
SKILL.md
11.9 KB, as published. Nobody here has run it
Spring Boot Security
When to use
This skill is the Spring-specific layer on top of secure-coding and api-security. Spring Security is powerful and exactly therefore foot-gun-rich: small letters in the config decide whether your app is safe or wide open.
Triggers on:
- A question like "review our Spring Security config", "OAuth2 client setup", "lock down actuator endpoints", "JWT validation on a Spring Resource Server", "@PreAuthorize review".
- Presence of
spring-boot-starter-security,spring-security-oauth2-client,SecurityFilterChainbeans,@EnableWebSecurity,@PreAuthorize/@PostAuthorizeannotations,application.ymlwithspring.security.*ormanagement.*. - A PR that touches
.permitAll(),.disable()on CSRF/CORS, customAuthenticationProvider,UserDetailsService, or a JWT filter. - Spring version bumps around security releases.
- A handoff from
security-revieworapi-securitywhere Spring is in the stack.
When NOT (handoff)
- General Java secure-coding →
secure-coding. - API design and OWASP API Top 10 — large overlap, but that skill is framework-agnostic. Use
api-securityfor conceptual questions (what is IDOR, how do you validate a schema), this skill for Spring-specific implementation. - SAST with Semgrep
p/java-springor CodeQL →sast-orchestrator. - Dep vulns (incl. Log4Shell-class vulns in transitive deps) →
cve-triage. - Infra around Spring (Kubernetes deploy, container image) →
container-hardening+k8s-security. - Secrets (application.properties with DB passwords) →
secrets-scanner.
Approach
Six phases. Phase 1 (SecurityFilterChain) is where most production bugs live.
1. SecurityFilterChain config
Spring Security 6+ uses the Lambda DSL. Each chain decides which auth mode goes with which path and what "open" means.
Common foot-guns:
.permitAll()on too broad a path.http.authorizeHttpRequests(auth -> auth.requestMatchers("/api/**").permitAll())opens the entire API. Look for.permitAll()on wildcards and challenge each one.- Order of matchers. The filter chain matches top-down. A
/api/**permitAll above a/api/admin/**authenticated rule overrules the latter. Always go from specific to general. .anyRequest().permitAll()— as the last rule this is catch-all open. Almost always wrong. The last rule should be.anyRequest().authenticated(), with an exception for a purely public app.- Multiple
SecurityFilterChainbeans with@Order. First match wins. A too-broad first chain can render later chains redundant. .csrf(csrf -> csrf.disable())without context. CSRF on by default for stateful web auth. Disabling is correct for stateless JWT APIs, wrong for form-based auth. If in doubt: leave it on.- CORS config.
.cors(cors -> cors.configurationSource(source))with aCorsConfigurationSourcethat returns wildcards is a standard misconception.allowedOrigins("*")together withallowCredentials(true)does not work (Spring rejects it), but it is a signal that the config flow has not been thought through.
Concrete reference config for a stateless JWT API:
@Bean
SecurityFilterChain api(HttpSecurity http) throws Exception {
http
.securityMatcher("/api/**")
.authorizeHttpRequests(auth -> auth
.requestMatchers(HttpMethod.GET, "/api/health").permitAll()
.requestMatchers("/api/admin/**").hasRole("ADMIN")
.anyRequest().authenticated())
.oauth2ResourceServer(rs -> rs.jwt(Customizer.withDefaults()))
.csrf(csrf -> csrf.disable()) // correct for stateless API
.sessionManagement(s -> s.sessionCreationPolicy(SessionCreationPolicy.STATELESS));
return http.build();
}
2. Method-level authorization
Route-level auth is never enough. Methods with sensitive logic deserve @PreAuthorize or equivalent.
@EnableMethodSecurity(Spring Security 6) replaces@EnableGlobalMethodSecurity. Without this annotation,@PreAuthorize/@PostAuthorizedo not work.@PreAuthorize("hasRole('ADMIN')")— role-based.@PreAuthorize("hasAuthority('SCOPE_write:documents')")— for JWT scopes.@PreAuthorize("#id == authentication.name or hasRole('ADMIN')")— per-resource ownership, the fix for IDOR (seeapi-securityAPI1 BOLA).@PostAuthorize— filter the return value after execution. Rarely needed, has performance impact.
Common miss: ownership check in the service method but not in the controller, or vice versa. One place is enough, but it must be unambiguous which one.
3. Actuator lockdown
Spring Boot Actuator exposes operational endpoints. The default config (older) was wide open. Since Boot 2.x the defaults are /health and /info public, the rest authenticated. Reviewer rule: confirm you have not fallen back to the older model.
management.endpoints.web.exposure.include— what gets exposed."*"is wrong in prod. Limit to what Ops actually needs:health,info,prometheus,metrics.management.endpoint.env.show-values=NEVERorALWAYS/WHEN_AUTHORIZED./envshows all env vars, including secrets when misconfigured.management.endpoint.heapdump.enabled=falsein prod. Heap dump via HTTP is a memory-exfil primitive.management.server.porton a separate port that is not externally routable. Plus firewall/NetworkPolicy (seek8s-security).- Actuator security on the SecurityFilterChain: a separate chain with
EndpointRequest.toAnyEndpoint()matcher, roleACTUATORor equivalent.
4. JWT and OAuth2
Spring has three OAuth2 roles: client (consumer), resource-server (you, validating tokens), authorization-server (you, issuing tokens). Each has its own foot-guns.
- Resource-server (JWT validation):
- Issuer validation required:
spring.security.oauth2.resourceserver.jwt.issuer-urior an explicitJwtDecoderwithNimbusJwtDecoder.withIssuerLocation(issuer). - Audience-claim validation must be built explicitly (not default).
OAuth2TokenValidatorFactories.create().andValidate(JwtIssuerValidator).andValidate(JwtAudienceValidator). - Algorithm whitelist: accept
RS256/ES256, rejectHS256unless explicitly intended. Algorithm confusion is a classic attack. See alsoapi-securityphase 2. - Clock skew:
.setClockSkew(Duration.ofMinutes(2))is reasonable, not 1 hour.
- Issuer validation required:
- Client (OAuth consumer):
- PKCE required for public clients (mobile, SPA that Spring only serves).
- Redirect URI strictly registered, no wildcards.
- Scopes minimal when requesting.
- Authorization-server:
- Spring Authorization Server is relatively young (stable since 2022). Use a vendor IdP (Keycloak, Auth0, Okta) unless you have a strong reason to self-host.
5. CVE patterns from recent years
The Spring ecosystem has a few infamous CVEs; each is a pattern to look for.
- Spring4Shell (CVE-2022-22965). Spring Framework RCE via class-loader manipulation in data binding. Patched in 5.2.20, 5.3.18. Historical but still relevant as a reviewer reflex: Java apps using
ServletRequestDataBinderwithout an allowlist are exposed. Spring Boot's default binder has been patched since the fix release. - CVE-2022-22963 Spring Cloud Function — SpEL injection via the
spring.cloud.function.routing-expressionheader. Lesson: SpEL evaluation on untrusted input is RCE. Search your code forSpelExpressionParser().parseExpression(userInput). - CVE-2023-20860 / -20861 Spring Framework — mass-binding and security bypass via
matcherscombined withmvcMatchers. Fixed in 5.3.26, 6.0.7. Reviewer rule: mixingantMatchersandmvcMatchersis a foot-gun — use one consistently and preferrequestMatchers(Spring Security 6). - CVE-2024-22257 Spring Security authorization bypass — possible bypass when
AuthenticatedVoterwas configured without additional checks.[verify against https://spring.io/security/cve-2024-22257]for the exact patched versions in your context. - More recent CVEs —
[verify against https://spring.io/security/]— check the CVE feed on every version bump or review. No invented IDs in findings.
6. Misc and verification-loop
@JsonIgnoreon sensitive entity fields (password_hash, internal notes) to keep them out of JSON responses (mass-response, the mirror of mass-assignment).@JsonProperty(access = WRITE_ONLY)for input-only fields.- Use DTOs instead of serializing entities directly. Prevents accidental field exposure when the DB schema changes.
- Session fixation: Spring Security prevents this by default (
SessionAuthenticationStrategy). Do not turn it off without reason. - Password hashing:
BCryptPasswordEncoderdefault. Argon2 variant viaArgon2PasswordEncoderif the library is included. NeverNoOpPasswordEncoderoutside tests.
Verification-loop: Layer 1 (SecurityFilterChain config coherent? Actuator endpoints explicitly locked down? JWT issuer + audience both validated?), Layer 2 (CVE IDs confirmed via spring.io/security, OAuth flow names correct, no invented Spring annotations in examples).
Output
Spring Security review — <service/module>
Spring Boot: <x.y.z> | Spring Security: <x.y.z> | Version status: <current | N releases behind>
SecurityFilterChain:
Chains present: N
permitAll() matchers: <list + context>
.anyRequest() last: <authenticated | permitAll — FINDING>
CSRF status: <enabled | disabled with context>
CORS config: <scoped | wildcard — FINDING>
Method security:
@EnableMethodSecurity: <yes/no>
@PreAuthorize coverage: <controllers with/without>
Ownership checks: <present on resource endpoints?>
Actuator:
Exposure: <list of endpoints>
/env show-values: <NEVER | WHEN_AUTHORIZED | ALWAYS — FINDING>
/heapdump: <disabled | exposed — FINDING>
Separate port or filter:<yes/no>
OAuth2 / JWT:
Role: <client | resource-server | both>
Issuer validation: <yes/no>
Audience validation: <yes/no>
Algorithm whitelist: <yes/no>
CVE check:
Spring4Shell patched: <yes>
Recent security release:<within N days of upstream?>
cve-triage handoff: <N open>
Findings (severity-sorted, follow security-review format)
Verification-loop: ...
References
- Spring Security Reference — https://docs.spring.io/spring-security/reference/. Canonical docs, Lambda DSL and config patterns.
- Spring Security CVE feed — https://spring.io/security/. All Spring projects CVEs, canonical source for verification.
- Spring Boot Actuator — https://docs.spring.io/spring-boot/reference/actuator/index.html. Endpoint config and security implications.
- OWASP Java Security Cheat Sheet — https://cheatsheetseries.owasp.org/cheatsheets/Java_Security_Cheat_Sheet.html.
- Spring Framework Reference (RequestMapping, Binding) — https://docs.spring.io/spring-framework/reference/. For patterns that include Spring4Shell-style attacks.
- RFC 8725 (JWT BCP) — https://datatracker.ietf.org/doc/html/rfc8725. JWT-specific gotchas.
- OAuth 2.0 Security BCP — https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics.
- NIST NVD — https://nvd.nist.gov/. For CVE verification on every cite.
Categories
- appsec