Panoramica Sicurezza
La postura di sicurezza di Flo è revisionata con la metodologia OWASP Top 10:2025 su tre round. Questa pagina sintetizza le protezioni effettivamente presenti nel codice: autenticazione, rate limiting, header, segreti a riposo e audit/forense, seguite dalla cronologia delle remediation e dalle regole operative che mantengono la postura aggiornata.
Protezioni di autenticazione
Login con password, login OTP e OIDC condividono lo stesso modello di sessione cookie. I flussi sono descritti in Autenticazione; i controlli rilevanti per la sicurezza sono:
- Hashing password con BCrypt. La policy è minimo 8 caratteri e
massimo 72 byte UTF-8 (troncamento BCrypt), più una blocklist offline di password
comuni e violate che rifiuta anche le mutazioni classiche
(
Password1234,p@ssw0rd!). VediPasswordHelperePasswordBlocklist. - Blocco login: 5 tentativi falliti attivano un blocco di 15 minuti per account.
I contatori vivono nella tabella
LoginAttempt, così container blue-green e riavvii di processo condividono un'unica vista; le righe stantie sono spazzate opportunisticamente e un login riuscito azzera il contatore. - Indurimento OTP: codici a 6 cifre, scadenza 10 minuti, 3 tentativi per codice, cooldown reinvio 30 s, confronto a tempo costante, hash a riposo e risposte identiche per email note e ignote (nessuna enumerazione).
- Memorizzazione token: i token di reset password e conferma email sono memorizzati come hash, non in chiaro.
- Protezioni di sessione: il cookie
FloAuthè HttpOnly, Secure, SameSite=Lax e cifrato con Data Protection. Oltre alla scadenza scorrevole,AuthSessionServiceimpone una durata assoluta di sessione di 7 giorni e invalidazione lato server (globale e per utente). Il logout revoca i refresh token OpenIddict e marca stantie le sessioni cookie; cambi e reset password invalidano le altre sessioni dell'utente. - Autorizzazione live:
LoginAccessMiddlewarericontrolla policy di login e claim di ruolo dell'utente a ogni richiesta autenticata, riemettendo il cookie quando i privilegi a DB sono cambiati. Le password operatore seminate (seed) impongono un cambio password al primo login: la dashboard redirige alla pagina di cambio e l'API risponde403finché la password non è ruotata. - Mitigazioni enumerazione: una verifica BCrypt fittizia uniforma il percorso account-inesistente, e il recupero password restituisce un unico messaggio per hit e miss.
Rate limiting
Il rate limiting è applicato a livello applicativo (policy rate limiter ASP.NET). Le policy principali:
| Policy | Partizione | Limite |
|---|---|---|
AuthEndpointsLimiter | IP client | 20/min (login, registrazione, recupero; spento in Development) |
PublicOtpValidateLimiter | IP client | 10/min |
PublicBrowserLimiter | IP client | 60/min (form pubblici, richiesta OTP) |
PublicIntegrationLimiter | hash token (o IP) | 300/min |
PaymentWebhookLimiter | IP client | 120/min |
BookingCheckoutLimiter / CommerceCheckoutLimiter | utente (o IP) | 10/min |
PresignRateLimiter | utente | 120/min |
MediaUploadRateLimiter | hash token (o IP) | 120/min |
PublicApiLimiter | finestra fissa | 10/min, 2 accodati |
I rifiuti restituiscono 429 con errors.general.rateLimitExceeded. Le partizioni sono
risolte da ClientIpResolver: l'header CF-Connecting-IP è onorato solo
quando TRUST_CF_CONNECTING_IP è abilitato e il peer immediato è dentro il perimetro
FORWARDED_PROXIES / FORWARDED_NETWORKS configurato, così un header falsificato
non può creare partizioni fresche a quota piena. Lo stato limiter è in-process:
oggi Flo gira con un'istanza backend per tenant, e lo scaling orizzontale richiederebbe
un backing store condiviso.
Anche nginx.conf.template definisce zone limit_req per IP, ma la flotta
di produzione gira su Docker + Traefik con i limiter applicativi; nginx è usato solo dal
percorso Compose singolo-tenant/locale (esclusione documentata X-07).
Header e CSP
Per i deploy nginx, nginx.conf.template imposta HSTS, Content-Security-Policy
(default-src 'self', allow-list hash per script, frame-ancestors 'self'),
X-Content-Type-Options, X-Frame-Options: SAMEORIGIN, Referrer-Policy,
Permissions-Policy (geolocalizzazione, microfono, camera disabilitati) e
X-Robots-Tag: noindex sulle API. Le location che dichiarano proprie
add_header ripetono il set di sicurezza, perché in quel caso nginx scarta gli header ereditati.
La SPA Cloudflare Pages distribuisce Flo.FE/public/_headers con nosniff,
X-Frame-Options, Referrer-Policy e Permissions-Policy. La CSP è differita
per la SPA perché la pagina di login carica SDK terzi (Google Identity
Services, Apple JS); il piano è distribuirla prima in Report-Only.
Le risposte API sono predefinite a no-store, il middleware eccezioni restituisce codici
di errore localizzati senza stack trace, e il banner server è soppresso.
Segreti a riposo
- Segreti tenant: la CLI cifra i valori segreti nei file
.envtenant con AES-256-GCM (tag autenticato, chiave derivata via scrypt), conservando ilmaster.keya0600. La rotazione è gestita da Rotazione dei Segreti. - Token auth webhook cifrati a riposo (ASP.NET Data Protection) e mai restituiti in chiaro.
- Token API memorizzati come hash SHA-256 più prefisso di 8 caratteri; i binding presign contengono hash dei token, non il token grezzo.
- Codici OTP e token di reset con hash a riposo; gli access token sono cifrati nello store OpenIddict.
- Backup con sidecar
.sha256verificati prima di un ripristino; dump pre-deploy creati a ogni deploy. - Supply chain: immagini container fissate per digest in CI e nel
Dockerfile,
npm ci --ignore-scriptsusato, dipendenze bloccate e un workflow SCA settimanale attivo.
Audit e forense
La superficie completa è descritta in Impostazioni e Audit. Proprietà chiave:
- La pista di audit è solo in accodamento: il DbContext rifiuta aggiornamenti ed eliminazioni
delle righe
AuditLog; le modifiche entità sono accodate nella stessa transazione della modifica con attore, azione, valori vecchi/nuovi e marca temporale. - La retention è predefinita a 730 giorni, configurabile tra 30 e 3650; le operazioni di anonimizzazione ed eliminazione accodano lapidi (tombstone) così il log resta auto-descrittivo dopo la redazione. Gli endpoint di export e anonimizzazione GDPR sono solo SuperAdmin.
- Gli eventi auth (
/api/v1/auth-events) registrano i fallimenti di login lato server e client mascherati; i fallimenti login con password e i blocchi sono registrati tramite la stessa giunzione. Il visualizzatore vive in Impostazioni > Accessi ed errori. - Log: file Serilog rotanti con visualizzatore solo SuperAdmin, export CSV/JSON
con neutralizzazione formule foglio di calcolo, redazione token in query-string
e PII mascherati nelle righe di log auth. Le richieste portano un header
X-Correlation-IDper il tracing.
Cronologia remediation OWASP
| Round | Obiettivo | Esito |
|---|---|---|
| 1 (2026-09-18) | Superficie API backend + infra (Flo.BE/**, Dockerfile, compose, nginx, CI) | 41 finding, 4 critici (endpoint log anonimi, token Jarvis consolidato, credenziali SuperAdmin da seed, esposizione log); tutti corretti e riverificati dinamicamente |
| 2 (2026-09-18) | Approfondimento: Flo.FE/src/**, cli/src/**, Flo.SchemaGen/** + replay fix round-1 | 22 finding corretti (XSS/returnUrl/upload FE, segreti argv CLI/estrazione backup, permessi SchemaGen) e verificati |
| 3 (2026-09-19) | Audit profondo full-repo su HEAD 1e60ce1 + clone live anonimizzato | 40 finding su 10 domini (39 confermati, 1 parziale), 1 critico (token Strapi consolidato); fix chiusi in bugfix/owasp-security-fixes con suite verdi e 9/9 prove dinamiche |
Fix notevoli del round-3: logout OIDC con revoca refresh token e sessioni
lato server, token OTP/reset con hash a riposo, blocklist password, log auth mascherati
e sink di log sanificati, blocco su DB condiviso tra container, cifratura token
webhook, cap assoluto sessione 7 giorni, lapidi audit, trust dei forwarder
da env FORWARDED_*, header di sicurezza Cloudflare Pages e immagini
di deploy fissate per digest.
Le esclusioni permanenti sono tracciate nel repository della piattaforma
(docs/SecurityExclusions.md): credenziali SuperAdmin da seed (X-01), Origin come
difesa in profondità solo per token API (X-02), MFA/step-up differiti (X-03),
alerting a livello SIEM (X-04), SDK social senza SRI (X-05), Flo.STRAPI fuori
perimetro (X-06) e nginx non usato in produzione (X-07). Un finding che ricade in
una di queste voci è citato come escluso, non riaperto silenziosamente.
Regole operative
- Mantieni aggiornate le checklist Pre-deploy/Post-deploy di
ReleaseNotes.md: sono il runbook di rilascio (vedi Processo di Rilascio). - Non deployare mai aggirando il preflight risorse, e non usare mai
--forcesenza autorizzazione esplicita per operazione. - Ruota i segreti su base pianificata e subito dopo ogni sospetta esposizione;
flo secrets rotateaccoda nuovi salt audit senza invalidare gli hash precedenti. - Completa un disarmo DR totale prima della manutenzione tenant o di un deploy MON: le guardie lease falliscono in modo chiuso e bloccano le scritture senza autorità valida. Vedi Failover e Disaster Recovery.
- Monitora le superfici che veicolano segnali di sicurezza: eventi auth, pista audit, log applicativi e avvisi di flotta (Monitoraggio, Control Plane).
- Non consolidare mai segreti o token. Una credenziale consolidata è un finding di sicurezza, non un dettaglio di configurazione.