Controlla la sessione con Spring Security

1. Panoramica

In questo articolo, illustreremo come Spring Security ci consente di controllare le nostre sessioni HTTP .

Questo controllo varia da un timeout di sessione all'abilitazione di sessioni simultanee e altre configurazioni di sicurezza avanzate.

2. Quando viene creata la sessione?

Possiamo controllare esattamente quando viene creata la nostra sessione e come Spring Security interagirà con essa:

  • sempre : verrà sempre creata una sessione se non ne esiste già una
  • ifRequired : verrà creata una sessione solo se necessario ( impostazione predefinita )
  • mai : il framework non creerà mai una sessione ma ne userà una se esiste già
  • stateless : nessuna sessione verrà creata o utilizzata da Spring Security
...

Configurazione Java:

@Override protected void configure(HttpSecurity http) throws Exception { http.sessionManagement() .sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED) }

È molto importante capire che questa configurazione controlla solo ciò che fa Spring Security, non l'intera applicazione. Spring Security potrebbe non creare la sessione se le chiediamo di non farlo, ma la nostra app potrebbe!

Per impostazione predefinita, Spring Security creerà una sessione quando ne ha bisogno - questo è " ifRequired ".

Per un'applicazione più senza stato , l' opzione " mai " garantirà che Spring Security stesso non creerà alcuna sessione; tuttavia, se l'applicazione ne crea uno, Spring Security lo utilizzerà.

Infine, l'opzione di creazione della sessione più rigorosa - " stateless " - è una garanzia che l'applicazione non creerà alcuna sessione .

Questo è stato introdotto nella Spring 3.1 e salterà effettivamente parti della catena di filtri Spring Security, principalmente le parti relative alla sessione come HttpSessionSecurityContextRepository , SessionManagementFilter , RequestCacheFilter .

Questi meccanismi di controllo più rigorosi hanno l'implicazione diretta che i cookie non vengono utilizzati e quindi ogni richiesta deve essere nuovamente autenticata . Questa architettura senza stato funziona bene con le API REST e il loro vincolo di apolidia. Funzionano bene anche con meccanismi di autenticazione come Basic e Digest Authentication.

3. Sotto il cofano

Prima di eseguire il processo di autenticazione, Spring Security eseguirà un filtro responsabile dell'archiviazione del contesto di sicurezza tra le richieste: SecurityContextPersistenceFilter . Il contesto verrà archiviato in base a una strategia, HttpSessionSecurityContextRepository per impostazione predefinita, che utilizza la sessione HTTP come archiviazione.

Per l' attributo rigoroso create-session = "stateless" , questa strategia verrà sostituita con un'altra - NullSecurityContextRepository - e nessuna sessione verrà creata o utilizzata per mantenere il contesto.

4. Controllo della sessione simultanea

Quando un utente già autenticato tenta di autenticarsi di nuovo , l'applicazione può gestire quell'evento in uno dei pochi modi. Può invalidare la sessione attiva dell'utente e autenticare nuovamente l'utente con una nuova sessione oppure consentire a entrambe le sessioni di esistere contemporaneamente.

Il primo passaggio per abilitare il supporto per il controllo della sessione simultaneo è aggiungere il seguente listener nel file web.xml :

  org.springframework.security.web.session.HttpSessionEventPublisher  

Oppure definiscilo come un fagiolo - come segue:

@Bean public HttpSessionEventPublisher httpSessionEventPublisher() { return new HttpSessionEventPublisher(); }

Ciò è essenziale per assicurarsi che il registro della sessione di Spring Security riceva una notifica quando la sessione viene distrutta .

Per abilitare lo scenario che consente più sessioni simultanee per lo stesso utente, il file elemento dovrebbe essere utilizzato nella configurazione XML:

Oppure, tramite la configurazione Java:

@Override protected void configure(HttpSecurity http) throws Exception { http.sessionManagement().maximumSessions(2) }

5. Timeout della sessione

5.1. Gestione del timeout della sessione

Dopo che la sessione è scaduta, se l'utente invia una richiesta con un ID di sessione scaduto , verrà reindirizzato a un URL configurabile tramite lo spazio dei nomi:

Allo stesso modo, se l'utente invia una richiesta con un ID di sessione che non è scaduto, ma del tutto non valido , verrà anche reindirizzato a un URL configurabile:

 ... 

La configurazione Java corrispondente:

http.sessionManagement() .expiredUrl("/sessionExpired.html") .invalidSessionUrl("/invalidSession.html");

5.2. Configurare il timeout della sessione con Spring Boot

Possiamo facilmente configurare il valore di timeout della sessione del server incorporato utilizzando le proprietà:

server.servlet.session.timeout=15m

Se non specifichiamo l'unità di durata, Spring assumerà che siano secondi.

In poche parole, con questa configurazione, dopo 15 minuti di inattività, la sessione scadrà. La sessione dopo questo periodo di tempo è considerata non valida.

Se abbiamo configurato il nostro progetto per utilizzare Tomcat, dobbiamo tenere presente che supporta solo la precisione al minuto per il timeout della sessione, con un minimo di un minuto. Ciò significa che se specifichiamo un valore di timeout di 170 s, ad esempio, risulterà in un timeout di 2 minuti.

Infine, è importante ricordare che anche se Spring Session supporta una proprietà simile per questo scopo ( spring.session.timeout ), se non è specificato, l'autoconfigurazione riporterà il valore della proprietà che abbiamo menzionato per la prima volta.

6. Impedire l'utilizzo di parametri URL per il monitoraggio della sessione

L'esposizione delle informazioni sulla sessione nell'URL è un rischio crescente per la sicurezza (dalla posizione 7 nel 2007 alla posizione 2 nel 2013 nella Top 10 List di OWASP).

A partire dalla Spring 3.0, la logica di riscrittura dell'URL che aggiungerebbe jsessionid all'URL può ora essere disabilitata impostando disable-url-rewriting = "true" nel spazio dei nomi.

In alternativa, a partire da Servlet 3.0, il meccanismo di tracciamento della sessione può essere configurato anche nel web.xml:

 COOKIE 

E a livello di codice:

servletContext.setSessionTrackingModes(EnumSet.of(SessionTrackingMode.COOKIE));

Questo sceglie dove memorizzare il JSESSIONID - nel cookie o in un parametro URL.

7. Protezione della fissazione della sessione con Spring Security

Il framework offre protezione contro i tipici attacchi di correzione della sessione configurando ciò che accade a una sessione esistente quando l'utente tenta di autenticarsi di nuovo:

 ...

La configurazione Java corrispondente:

http.sessionManagement() .sessionFixation().migrateSession()

Per impostazione predefinita, Spring Security ha questa protezione abilitata (" migrateSession "): all'autenticazione viene creata una nuova sessione HTTP, quella vecchia viene invalidata e gli attributi della vecchia sessione vengono copiati.

Se questo non è il comportamento desiderato, sono disponibili altre due opzioni:

  • quando è impostato " nessuno ", la sessione originale non verrà invalidata
  • when “newSession” is set, a clean session will be created without any of the attributes from the old session being copied over

8. Secure Session Cookie

Next, we'll discuss how to secure our session cookie.

We can use the httpOnly and secure flags to secure our session cookie:

  • httpOnly: if true then browser script won't be able to access the cookie
  • secure: if true then the cookie will be sent only over HTTPS connection

We can set those flags for our session cookie in the web.xml:

 1  true true  

This configuration option is available since Java servlet 3. By default, http-only is true and secure is false.

Let's also have a look at the corresponding Java configuration:

public class MainWebAppInitializer implements WebApplicationInitializer { @Override public void onStartup(ServletContext sc) throws ServletException { // ... sc.getSessionCookieConfig().setHttpOnly(true); sc.getSessionCookieConfig().setSecure(true); } }

If we're using Spring Boot, we can set these flags in our application.properties:

server.servlet.session.cookie.http-only=true server.servlet.session.cookie.secure=true

Finally, we can also achieve this manually by using a Filter:

public class SessionFilter implements Filter { @Override public void doFilter( ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest req = (HttpServletRequest) request; HttpServletResponse res = (HttpServletResponse) response; Cookie[] allCookies = req.getCookies(); if (allCookies != null) { Cookie session = Arrays.stream(allCookies).filter(x -> x.getName().equals("JSESSIONID")) .findFirst().orElse(null); if (session != null) { session.setHttpOnly(true); session.setSecure(true); res.addCookie(session); } } chain.doFilter(req, res); } }

9. Working With the Session

9.1. Session Scoped Beans

A bean can be defined with session scope simply by using the @Scope annotation on beans declared in the web-Context:

@Component @Scope("session") public class Foo { .. }

Or with XML:

Then, the bean can simply be injected into another bean:

@Autowired private Foo theFoo;

And Spring will bind the new bean to the lifecycle of the HTTP Session.

9.2. Injecting the Raw Session into a Controller

The raw HTTP Session can also be injected directly into a Controller method:

@RequestMapping(..) public void fooMethod(HttpSession session) { session.setAttribute(Constants.FOO, new Foo()); //... Foo foo = (Foo) session.getAttribute(Constants.FOO); }

9.3. Obtaining the Raw Session

La sessione HTTP corrente può anche essere ottenuta in modo programmatico tramite l' API Servlet raw :

ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes(); HttpSession session= attr.getRequest().getSession(true); // true == allow create

10. Conclusione

In questo articolo abbiamo discusso della gestione delle sessioni con Spring Security. Inoltre, Spring Reference contiene un'ottima FAQ sulla gestione delle sessioni.

Come sempre, il codice presentato in questo articolo è disponibile su Github. Questo è un progetto basato su Maven, quindi dovrebbe essere facile da importare ed eseguire così com'è.