skip to content

Web Exploit & Session Protections

The browser-facing defences Spring Security turns on: CSRF, CORS, session management and fixation, concurrent sessions, remember-me, and security headers. Interviewers ask because 'I disabled CSRF to make it work' is such a common confession.

part ofSpring Frameworkoverview, primer and where to startread it →
on this pageshow

explore

questions

30

What is CORS, and how do you enable it in a Spring Security application?

level: juniorimportance: must knowfreq 70%

answer

  1. http.cors(Customizer.withDefaults())
  2. CorsConfigurationSource bean named corsConfigurationSource
  3. browser-only, opt-in via Access-Control-Allow-*
  4. filter chain runs before MVC
  5. UrlBasedCorsConfigurationSource per path

basics

~20 s

CORS lets a browser page from one origin call an API on another origin. In Spring Security you turn it on with http.cors() in the SecurityFilterChain and provide a CorsConfigurationSource bean that lists allowed origins, methods, and headers.

solid answer

~30 s

CORS (Cross-Origin Resource Sharing) is a browser rule: JavaScript on origin A (scheme+host+port) may only read a response from origin B if B sends Access-Control-Allow-* headers permitting it. In Spring Security you enable it inside the SecurityFilterChain with http.cors(Customizer.withDefaults()). That activates Spring Security's CorsFilter, which reads a CorsConfigurationSource bean (usually named corsConfigurationSource). You typically register a UrlBasedCorsConfigurationSource with a CorsConfiguration specifying setAllowedOrigins, setAllowedMethods, setAllowedHeaders, and optionally setAllowCredentials. Without http.cors(), Spring Security ignores your CORS config for anything the filter chain protects, and the preflight OPTIONS request can be blocked by authorization before your controller ever runs.

code

java · 23 lines
java
@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .cors(Customizer.withDefaults())   // wires in the CorsFilter using the bean below
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());
        return http.build();
    }

    @Bean
    CorsConfigurationSource corsConfigurationSource() {
        CorsConfiguration config = new CorsConfiguration();
        config.setAllowedOrigins(List.of("https://app.example.com"));
        config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE"));
        config.setAllowedHeaders(List.of("Authorization", "Content-Type"));
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", config);
        return source;
    }
}

go deeper

for a junior

Must know CORS is a browser cross-origin rule and that http.cors() plus a CorsConfigurationSource bean enables it.

for a middle

Should explain why the security chain (not @CrossOrigin) is the right place and what the source bean contains.

for a senior

Should distinguish CORS from authz and know the bean-name resolution and per-path source mapping.

for a principal

Frames CORS as one browser-side control among many, and reasons about same-origin deployment as the way to avoid it entirely.

## What CORS is An **origin** is the triple scheme + host + port (e.g. `https://app.example.com:443`). Browsers enforce the **Same-Origin Policy**: JavaScript loaded from origin A cannot, by default, read responses from origin B. **CORS (Cross-Origin Resource Sharing)** is the standard by which server B *opts in* to being called cross-origin by sending `Access-Control-Allow-*` response headers. CORS is enforced entirely by the **browser** — a non-browser client (curl, another server) ignores it. So CORS is not authentication or authorization; it only controls which web pages may *read* your responses in a browser. ## Enabling it in Spring Security Spring Security has its own CORS integration because the **security filter chain runs before Spring MVC**. You enable it in your `SecurityFilterChain`: ```java http.cors(Customizer.withDefaults()); ``` `Customizer.withDefaults()` tells the `CorsConfigurer` to look up a bean of type `CorsConfigurationSource` (by default the bean named `corsConfigurationSource`) and install a `CorsFilter` into the chain that uses it. ## The CorsConfigurationSource bean ```java @Bean CorsConfigurationSource corsConfigurationSource() { CorsConfiguration config = new CorsConfiguration(); config.setAllowedOrigins(List.of("https://app.example.com")); config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE")); config.setAllowedHeaders(List.of("Authorization", "Content-Type")); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration("/**", config); return source; } ``` `CorsConfiguration` is the object that becomes the `Access-Control-Allow-*` headers. `UrlBasedCorsConfigurationSource` maps URL patterns to configurations, so you can have different rules per path. ## Why not just @CrossOrigin or a WebMvcConfigurer? `@CrossOrigin` on a controller and `WebMvcConfigurer.addCorsMappings` configure CORS at the **MVC layer**, which sits *after* the security filter chain. If Spring Security rejects the request first (for example a preflight `OPTIONS` that has no credentials), your controller-level CORS config never runs. That is why, when Spring Security is on the classpath, you must configure CORS through `http.cors()` so the `CorsFilter` sits inside the security chain. ## Common gotchas - Forgetting `http.cors()` — the bean exists but is never wired into the security chain. - Wrong bean name/type — `Customizer.withDefaults()` resolves `CorsConfigurationSource`; a mismatched name is silently ignored. - Setting allowed origins to `*` together with credentials (illegal — covered in a separate question). ## When to use Any time a browser SPA served from one origin (e.g. `localhost:3000` or a CDN domain) calls your Spring API on a different origin. If the UI and API share an origin (same host/port, e.g. served behind one reverse proxy), you do not need CORS at all.

  • Does CORS protect your server from malicious clients?
    No. CORS is enforced by the browser and only governs whether page JavaScript may read a response. A non-browser client (curl, another backend) ignores CORS entirely, so it is not a substitute for authentication/authorization.
  • What happens if you define the CorsConfigurationSource bean but forget http.cors()?
    The security filter chain never installs the CorsFilter, so no Access-Control-Allow-* headers are added and cross-origin browser calls fail. The bean is effectively dead configuration.

saying these in an interview costs you the question

  • Claiming CORS blocks malicious/non-browser clients or replaces authentication
  • Saying @CrossOrigin alone is enough when Spring Security is present
  • Thinking CORS is a server-to-server restriction rather than a browser policy

context

open as a page

What is CSRF, and how does Spring Security protect against it by default?

level: juniorimportance: must knowfreq 72%

basics

~10 s

CSRF tricks a logged-in user's browser into sending an unwanted state-changing request using their cookies. Spring Security defends with a secret token (CsrfFilter) that must accompany every POST/PUT/DELETE/PATCH, which an attacker's site cannot know.

open as a page

Which HTTP security response headers does Spring Security add by default, and where does that behavior come from?

level: juniorimportance: must knowfreq 70%

basics

~10 s

By default Spring Security adds protective response headers automatically: Cache-Control (no-store), X-Content-Type-Options: nosniff, X-Frame-Options: DENY, and Strict-Transport-Security (HSTS) on HTTPS requests. You don't configure anything to get them.

open as a page

What is session fixation and how does Spring Security protect against it by default?

level: juniorimportance: must knowfreq 60%

basics

~20 s

Session fixation is when an attacker forces a victim to use a known session ID, then hijacks it after login. Spring Security defends by changing the session ID at login (changeSessionId), so the old ID is useless.

open as a page

What is the difference between maxSessionsPreventsLogin(true) and (false)?

level: middleimportance: must knowfreq 55%

basics

~20 s

With false (default) a new login is allowed and the oldest session is expired. With true the new login is refused (throws SessionAuthenticationException) and existing sessions stay alive — old sessions win over new ones.

open as a page

Why must Spring Security's CorsFilter be ordered before the authorization filter, and how does that affect preflight OPTIONS requests?

level: middleimportance: must knowfreq 65%

basics

~20 s

Browsers send a preflight OPTIONS request with no credentials before the real cross-origin call. Spring Security's CorsFilter runs early, before authorization, so it can answer the preflight and permit it instead of the authorization filter rejecting it as unauthenticated.

open as a page

Explain SessionCreationPolicy STATELESS vs IF_REQUIRED and when you would choose each.

level: middleimportance: must knowfreq 70%

basics

~20 s

IF_REQUIRED (the default) lets Spring create an HttpSession only when needed, e.g. to remember a logged-in user. STATELESS never creates or uses a session — each request must carry its own credentials, typical for token/JWT APIs.

open as a page

Why is HttpSessionEventPublisher required for concurrent session control, and what breaks without it?

level: seniorimportance: must knowfreq 50%

basics

~20 s

HttpSessionEventPublisher is a servlet listener that turns container session create/destroy events into Spring events so SessionRegistryImpl knows when sessions end. Without it, ended sessions stay counted in the registry and users can be falsely locked out.

open as a page

How do allowedOrigins, allowedOriginPatterns, and allowCredentials interact, and what is the wildcard-plus-credentials pitfall?

level: seniorimportance: must knowfreq 60%

basics

~20 s

If allowCredentials is true you cannot use "*" for origins, because the browser refuses Access-Control-Allow-Origin: * together with credentials. Either list exact origins with setAllowedOrigins, or use setAllowedOriginPatterns to match wildcards while still reflecting a concrete origin.

open as a page

When is it correct to disable CSRF protection for a stateless bearer-token API, and why is it safe there but dangerous with cookie auth?

level: seniorimportance: must knowfreq 76%

basics

~20 s

Disable CSRF only when auth uses a bearer token in the Authorization header and no cookies. Browsers don't auto-attach that header cross-site, so there's nothing to forge. If auth uses cookies (session or JWT-in-cookie), keep CSRF on.

open as a page

Compare PersistentTokenBasedRememberMeServices with TokenBasedRememberMeServices. When would you choose persistent tokens?

level: seniorimportance: must knowfreq 50%

basics

~20 s

TokenBased is a stateless signed cookie with no storage but no per-device revocation. Persistent stores a series/token row per login in a database, rotates the token on each use, allows revoking single devices, and can detect a stolen (cloned) cookie. Choose persistent when you need revocation and theft detection.

open as a page

What is concurrent session control in Spring Security and how do you limit a user to one active session?

level: juniorimportance: should knowfreq 45%

basics

~10 s

Concurrent session control limits how many simultaneous HTTP sessions one authenticated user can have. In the security config you call sessionManagement().maximumSessions(1) so a second login is either blocked or expires the older session.

open as a page

What is the 'remember-me' feature in Spring Security, and how do you enable it?

level: juniorimportance: should knowfreq 45%

basics

~10 s

Remember-me keeps a user logged in across browser restarts by sending a long-lived cookie instead of relying only on the session. You enable it with the rememberMe() method in the SecurityFilterChain configuration.

open as a page

What is X-Frame-Options, how does Spring Security set it by default, and how do you change it (e.g. for an H2 console or same-origin iframe)?

level: middleimportance: should knowfreq 55%

basics

~10 s

X-Frame-Options controls whether your page can be embedded in a frame, preventing clickjacking. Spring Security defaults to DENY. To allow same-origin framing (e.g. the H2 console) use headers().frameOptions(frame -> frame.sameOrigin()).

open as a page

Compare the session-fixation strategies (changeSessionId, migrateSession, newSession, none) and when each is appropriate.

level: middleimportance: should knowfreq 35%

basics

~10 s

changeSessionId keeps the session and its data but gives it a new ID (default). migrateSession makes a new session and copies attributes. newSession makes a fresh empty session. none disables rotation and is unsafe.

open as a page

Walk through the components that enforce concurrent session control: SessionRegistry, the authentication strategies, and ConcurrentSessionFilter.

level: seniorimportance: should knowfreq 40%

basics

~20 s

At login, ConcurrentSessionControlAuthenticationStrategy checks the SessionRegistry count and enforces the limit, then RegisterSessionAuthenticationStrategy records the new session. On every request, ConcurrentSessionFilter checks if the session was marked expired and, if so, logs out and redirects.

open as a page

In a Spring Security app, why configure CORS via the CorsConfigurationSource in the filter chain rather than WebMvcConfigurer.addCorsMappings or @CrossOrigin?

level: seniorimportance: should knowfreq 45%

basics

~20 s

The security filter chain runs before Spring MVC. If CORS lives only in WebMvcConfigurer or @CrossOrigin, the security filters can reject a preflight before MVC handles it. Configuring a CorsConfigurationSource plus http.cors() puts CORS inside the filter chain where it runs first.

open as a page

In Spring Security 6, how does CSRF token handling work (deferred tokens, XorCsrfTokenRequestAttributeHandler, BREACH), and what breaks in a SPA if you don't account for it?

level: seniorimportance: should knowfreq 40%

basics

~20 s

Spring Security 6 defers (lazily loads) the CsrfToken and, by default, XORs it with random bytes per request via XorCsrfTokenRequestAttributeHandler for BREACH protection. In a SPA this means the cookie holds the raw token but the header must be handled correctly, so you often add a filter to force token loading and a request handler that resolves the plain header value.

open as a page

How do you configure Content-Security-Policy in Spring Security, why isn't it on by default, and how does report-only mode work?

level: seniorimportance: should knowfreq 45%

basics

~10 s

CSP restricts which sources a page may load scripts/styles/etc. from, mitigating XSS. It isn't a Spring default because policies are app-specific. Configure it with headers().contentSecurityPolicy(csp -> csp.policyDirectives("default-src 'self'")).

open as a page

Explain HSTS (Strict-Transport-Security): Spring Security's defaults, its directives, and the gotchas around when it is emitted.

level: seniorimportance: should knowfreq 50%

basics

~10 s

HSTS tells browsers to always use HTTPS for a host. Spring Security sends Strict-Transport-Security: max-age=31536000; includeSubDomains, but only on secure requests. Tune it via httpStrictTransportSecurity(hsts -> hsts.maxAgeInSeconds(...).includeSubDomains(...).preload(...)).

open as a page

Where does RememberMeAuthenticationFilter sit in the filter chain, and how does it turn a cookie into an authenticated user?

level: seniorimportance: should knowfreq 40%

basics

~20 s

RememberMeAuthenticationFilter runs late in the chain, after normal login filters but before the anonymous filter. If no user is already authenticated, it reads the remember-me cookie via RememberMeServices.autoLogin(), and if valid it authenticates a RememberMeAuthenticationToken through the AuthenticationManager and stores it in the SecurityContext.

open as a page

How does Spring Security persist the SecurityContext across requests in a stateful web app?

level: seniorimportance: should knowfreq 45%

basics

~10 s

It uses HttpSessionSecurityContextRepository, which saves the SecurityContext under an HttpSession attribute after a request and reloads it at the start of the next one, so the logged-in user stays authenticated across requests.

open as a page

Why does default concurrent session control break in a clustered/multi-instance deployment, and how do you fix it?

level: principalimportance: should knowfreq 30%

basics

~20 s

The default SessionRegistryImpl is an in-memory map local to one JVM, so each node counts only its own sessions. Across nodes the limit isn't truly enforced. Fix it with a shared session store like Spring Session (Redis/JDBC) and SpringSessionBackedSessionRegistry.

open as a page

Designing CORS for a cookie-based (refresh-token) JWT SPA on a separate origin: what must be configured and hardened, and what are the caching and header pitfalls?

level: principalimportance: should knowfreq 35%

basics

~20 s

Set an exact origin allowlist (or a tight pattern), setAllowCredentials(true) so cookies flow, and expose only needed response headers via setExposedHeaders. Keep the origin list minimal, pair with SameSite cookies and CSRF defenses, and rely on Vary: Origin for correct caching.

open as a page

You're designing remember-me for a security-sensitive production app. What are the key risks and how do you harden the configuration?

level: principalimportance: should knowfreq 30%

basics

~20 s

The core risk is a stolen long-lived cookie granting account access. Harden by serving only over HTTPS with Secure and HttpOnly cookies, using persistent tokens for revocation and theft detection, keeping validity short, setting a stable secret key, and gating sensitive actions behind fullyAuthenticated so remember-me users must re-enter their password.

open as a page

You built a STATELESS JWT API but SecurityContextHolder.setAuthentication() from a controller doesn't stick across requests, and a JSESSIONID cookie still shows up. Diagnose and design the correct approach.

level: principalimportance: should knowfreq 30%

basics

~20 s

STATELESS means Spring Security never stores the context in a session, so setting it in a controller can't persist — you must re-establish auth every request via a token filter. A JSESSIONID appears because other code (CSRF, MVC, request.getSession) still creates sessions.

open as a page

How do SameSite cookies and CSRF tokens relate as defenses, and why is SameSite not a full replacement for Spring's CSRF protection?

level: principalimportance: nice to knowfreq 30%

basics

~20 s

SameSite=Lax/Strict tells the browser not to send the cookie on cross-site requests, blocking most CSRF at the cookie layer. But it's browser-dependent, Lax still allows top-level GET navigations, and coverage varies, so keep Spring's CSRF token as defense in depth.

open as a page

Configure Referrer-Policy in Spring Security and articulate an overall security-headers hardening strategy (defaults, nosniff, cache-control, X-XSS-Protection).

level: principalimportance: nice to knowfreq 35%

basics

~10 s

Referrer-Policy controls how much of the current URL is sent in the Referer header on navigation/requests. It's opt-in: headers().referrerPolicy(r -> r.policy(ReferrerPolicy.SAME_ORIGIN)). Combine it with the default nosniff, X-Frame-Options, HSTS, and cache-control for layered hardening.

open as a page