La Sicurezza come Priorità Architetturale
In una piattaforma di gestione eventi come Play The Event, la sicurezza non è un aspetto secondario: è un requisito fondamentale. Gli utenti condividono dati personali, informazioni di pagamento, documenti e comunicazioni private. Proteggere questi dati richiede un approccio multilivello che copra autenticazione, autorizzazione, protezione dei dati in transito e a riposo, e difesa contro le principali categorie di attacchi web.
Cosa Troverai in Questo Articolo
- L'architettura di autenticazione JWT con HttpOnly cookies
- La gestione stateless delle sessioni
- Il meccanismo di refresh token con rotazione
- Il workflow di verifica email
- Il sistema RBAC (Role-Based Access Control)
- Gli header di sicurezza e le protezioni OWASP
- Il rate limiting e la validazione degli input
JWT con HttpOnly Cookies
La scelta più importante nel design dell'autenticazione è stata quella di non memorizzare i token JWT nel localStorage. Nonostante sia la pratica più diffusa nelle SPA (Single Page Application), il localStorage è vulnerabile ad attacchi XSS: qualsiasi script JavaScript che riesce a eseguire nel contesto della pagina può leggere il token e inviarlo a un server malevolo.
Play The Event utilizza invece HttpOnly cookies, che non sono accessibili da JavaScript e vengono inviati automaticamente dal browser con ogni richiesta HTTP verso il dominio corretto.
// Creazione del cookie HttpOnly con il token JWT
ResponseCookie accessCookie = ResponseCookie.from("access_token", accessToken)
.httpOnly(true) // Non accessibile da JavaScript
.secure(true) // Solo HTTPS
.sameSite("Strict") // Protezione CSRF
.path("/") // Valido per tutto il dominio
.maxAge(Duration.ofMinutes(15)) // Scadenza breve
.build();
ResponseCookie refreshCookie = ResponseCookie.from("refresh_token", refreshToken)
.httpOnly(true)
.secure(true)
.sameSite("Strict")
.path("/api/v1/auth/refresh") // Solo per il refresh endpoint
.maxAge(Duration.ofDays(7)) // Scadenza lunga
.build();
Sicurezza: localStorage vs HttpOnly Cookies
Memorizzare token JWT nel localStorage li espone a qualsiasi vulnerabilità
XSS presente nell'applicazione o nelle librerie di terze parti. Un singolo script malevolo
può rubare il token e impersonare l'utente. Gli HttpOnly cookies eliminano completamente
questo vettore di attacco, perché il browser gestisce i token senza mai esporli al
codice JavaScript.
Gestione Stateless delle Sessioni
L'architettura di autenticazione è completamente stateless: il server non mantiene alcuna sessione in memoria. Ogni richiesta HTTP contiene tutte le informazioni necessarie per autenticare e autorizzare l'utente, codificate nel token JWT.
Questo approccio presenta vantaggi significativi per la scalabilità: è possibile distribuire il carico su più istanze del server senza bisogno di session replication o sticky sessions. Ogni istanza può validare indipendentemente il token JWT verificando la firma digitale.
// Esempio di payload JWT (i dati sensibili NON sono inclusi)
{
"sub": "user-uuid-here",
"email": "utente@example.com",
"roles": ["USER", "ORGANIZER"],
"iat": 1709294400,
"exp": 1709295300,
"jti": "unique-token-id"
}
Token Refresh con Rotazione
Il sistema implementa un meccanismo di refresh token rotation che previene gli attacchi di riutilizzo dei token. Quando un refresh token viene utilizzato per ottenere un nuovo access token, il vecchio refresh token viene invalidato e ne viene emesso uno nuovo.
FLUSSO REFRESH TOKEN
1. Client invia richiesta con access_token scaduto
→ Server risponde 401 Unauthorized
2. Client chiama POST /api/v1/auth/refresh
→ Il refresh_token viene inviato automaticamente via cookie
3. Server verifica il refresh_token:
a. Token valido e non nella blacklist?
b. Token non ancora utilizzato per un refresh?
c. Utente ancora attivo nel sistema?
4. Se tutto è valido:
→ Genera nuovo access_token (15 min)
→ Genera nuovo refresh_token (7 giorni)
→ Invalida il vecchio refresh_token (blacklist)
→ Imposta nuovi cookie HttpOnly
5. Se il refresh_token è già stato utilizzato:
→ ALERT: possibile furto token
→ Invalida TUTTI i refresh token dell'utente
→ L'utente deve riautenticarsi
La rotazione garantisce che ogni refresh token possa essere usato una sola volta. Se un attaccante riesce a rubare un refresh token e lo usa, il legittimo proprietario riceverà un errore al prossimo refresh, segnalando una possibile compromissione.
Verifica Email
La registrazione richiede la verifica dell'indirizzo email attraverso un token monouso inviato alla casella di posta dell'utente. Il token ha una scadenza di 24 ore e può essere usato una sola volta. Fino alla verifica, l'account ha funzionalità limitate.
Role-Based Access Control (RBAC)
Il sistema RBAC di Play The Event opera su due livelli: i ruoli di sistema e i ruoli per evento.
Ruoli di Sistema
- ADMIN: accesso completo a tutte le funzionalità della piattaforma, gestione utenti, moderazione contenuti e accesso ai pannelli amministrativi
- USER: utente standard che può creare e partecipare a eventi, gestire il proprio profilo e utilizzare tutte le funzionalità base
- ORGANIZER: ruolo esteso con accesso a funzionalità premium come analytics avanzati, integrazioni esterne e gestione eventi su larga scala
- MODERATOR: può moderare contenuti pubblici, gestire segnalazioni e applicare le policy della piattaforma
Sicurezza a Livello di Metodo
Oltre ai filtri HTTP, il sistema utilizza @PreAuthorize di Spring Security per
proteggere i singoli metodi dei controller, garantendo un controllo granulare sulle autorizzazioni.
@RestController
@RequestMapping("/api/v1/eventi")
public class EventoController {
@PostMapping
@PreAuthorize("hasAnyRole('USER', 'ORGANIZER')")
public ResponseEntity<EventoResponse> createEvento(
@Valid @RequestBody CreateEventoRequest request) {
// Solo utenti con ruolo USER o ORGANIZER
}
@DeleteMapping("/{id}")
@PreAuthorize("@eventoSecurityService.isOrganizer(#id, authentication)")
public ResponseEntity<Void> deleteEvento(@PathVariable Long id) {
// Solo l'organizzatore dell'evento specifico
}
@GetMapping("/admin/all")
@PreAuthorize("hasRole('ADMIN')")
public ResponseEntity<Page<EventoResponse>> getAllEventi(Pageable pageable) {
// Solo amministratori
}
}
Security Headers
La piattaforma implementa un set completo di header HTTP di sicurezza per proteggere contro le principali categorie di attacchi web.
HEADER DI SICUREZZA
Content-Security-Policy:
default-src 'self';
script-src 'self';
style-src 'self' 'unsafe-inline';
img-src 'self' data: https:;
connect-src 'self' https://api.stripe.com;
X-Frame-Options: DENY
→ Previene clickjacking (embedding in iframe)
X-Content-Type-Options: nosniff
→ Previene MIME type sniffing
Strict-Transport-Security: max-age=31536000; includeSubDomains
→ Forza HTTPS per un anno
Referrer-Policy: strict-origin-when-cross-origin
→ Limita le informazioni nel referrer header
Set-Cookie: SameSite=Strict
→ Protezione CSRF nativa del browser
Rate Limiting con Bucket4j
Per prevenire abusi, attacchi brute-force e DDoS, il sistema implementa rate limiting utilizzando la libreria Bucket4j, basata sull'algoritmo token bucket.
- API generali: 100 richieste al minuto per utente autenticato
- Endpoint di login: 5 tentativi al minuto per IP (protezione brute-force)
- Endpoint di registrazione: 3 richieste al minuto per IP
- Endpoint di refresh: 10 richieste al minuto per utente
Best Practice: Rate Limiting Differenziato
Non tutti gli endpoint hanno le stesse esigenze di protezione. Gli endpoint di autenticazione richiedono limiti molto più restrittivi rispetto alle API di lettura dati. Il rate limiting deve essere proporzionato al rischio: più l'endpoint è sensibile, più il limite deve essere basso. Bucket4j permette di configurare limiti diversi per gruppo di endpoint.
Hashing delle Password con BCrypt
Le password degli utenti sono hashate con BCrypt utilizzando un cost factor di 12, che garantisce circa 250ms per operazione di hashing. Questo rende computazionalmente costosi gli attacchi brute-force anche in caso di compromissione del database.
Validazione degli Input
Ogni dato in ingresso al sistema è validato utilizzando Jakarta Bean Validation
(annotations @NotNull, @Size, @Email,
@Pattern) prima di raggiungere la logica di business. I DTO agiscono come
primo livello di difesa, mentre i Value Objects nel dominio forniscono un secondo livello
di validazione semantica.
Conformità OWASP Top 10
Il sistema è stato progettato tenendo in considerazione le OWASP Top 10 vulnerabilità più critiche per le applicazioni web.
- A01 - Broken Access Control: RBAC multilivello, validazione proprietario risorse, principio del minimo privilegio
- A02 - Cryptographic Failures: HTTPS forzato, BCrypt per password, crittografia dei dati sensibili a riposo
- A03 - Injection: parametrizzazione query via JPA/Hibernate, escape automatico degli input
- A04 - Insecure Design: architettura threat-modeled, security by design dall'inizio del progetto
- A05 - Security Misconfiguration: header di sicurezza completi, configurazioni diverse per ambiente (dev, staging, prod)
- A07 - Authentication Failures: JWT HttpOnly, refresh rotation, lockout dopo tentativi falliti
Token Blacklist per il Logout
Il logout invalida immediatamente i token dell'utente inserendoli in una blacklist. Ogni richiesta autenticata verifica che il token non sia nella blacklist prima di procedere. La blacklist è implementata con una cache in-memory con TTL uguale alla durata massima del token, evitando una crescita illimitata.
Riepilogo delle Protezioni
- JWT in HttpOnly cookies con SameSite=Strict
- Refresh token rotation con rilevamento furto
- RBAC a due livelli (sistema e per-evento)
- Security headers completi (CSP, HSTS, X-Frame-Options)
- Rate limiting differenziato con Bucket4j
- BCrypt con cost factor 12
- Validazione input a due livelli (DTO + Value Objects)
- Token blacklist per logout immediato
Il codice sorgente relativo alla sicurezza è disponibile su GitHub. Nel prossimo articolo approfondiremo la gestione degli eventi e dei partecipanti, esplorando le API REST, la macchina a stati e il sistema di inviti di Play The Event.







