Skip to content

Security: AngeloRubens/alifaces

Security

SECURITY.md

Sicurezza e criteri di esposizione

Stato

Questa repository è una POC con hardening dimostrativo, non un'affermazione di sicurezza assoluta. Nessun cookie è “inattaccabile”: HTTPS, browser, endpoint, gestione delle chiavi, dipendenze e infrastruttura fanno parte dello stesso confine di sicurezza.

La demo può essere esposta per finalità editoriali in un ambiente isolato, con dati e account fittizi. Non deve accettare pagamenti, dati personali o ordini reali.

Cosa contengono i cookie

Cookie Contenuto Flag Rischio residuo
alifacesAuth JWT HS256 con sub, iss, aud, jti, iat, nbf, exp; nessun nome/e-mail HttpOnly, Secure su HTTPS, SameSite=Lax, path dell'app Un JWT rubato è riutilizzabile fino alla scadenza (1 ora)
alifacesShopper Identificatore guest opaco casuale a 256 bit stessi flag È una capability: chi lo ruba vede/modifica il carrello guest
amaFacesTheme nome tema validato su whitelist HttpOnly, Secure su HTTPS, SameSite=Lax Nessun dato affidabile o sensibile
alifacesLang codice lingua validato su whitelist (en, it) stessi flag Nessun dato affidabile o sensibile
alifacesNotice chiave di bundle + argomenti del messaggio da mostrare dopo un redirect, durata 60 s stessi flag Chi lo falsifica mostra a sé stesso un messaggio dell'applicazione: la chiave è validata sul bundle, gli argomenti sono limitati e l'output è escapato
BFCache-Buster numero opaco scritto da OmniFaces CacheControlFilter per invalidare la bfcache del browser HttpOnly, SameSite=Lax, host-only, cookie di sessione Nessun dato applicativo; non è emesso da questo codice

Tutti i cookie applicativi passano da CookiePolicy, che decide gli attributi in un punto solo: HttpOnly sempre, nessun Domain (host-only), Path limitato all'applicazione, e Secure quando la richiesta è HTTPS o quando un reverse proxy dichiara X-Forwarded-Proto: https. Fidarsi dell'header inoltrato può solo aggiungere il flag, quindi un client che mente danneggia unicamente sé stesso.

Il flag non viene forzato dallo stage Production. Quella regola sembrava prudente ed era peggio del problema che voleva risolvere: un deployment in Production servito in chiaro emetteva cookie che il browser scarta in silenzio, quindi ogni richiesta arrivava con un'identità nuova e il cliente vedeva il carrello sempre vuoto, senza una riga di log a spiegarlo. Ora in quel caso parte un WARNING all'avvio — la mancanza del flag su un deployment in chiaro è un allarme, non un'applicazione rotta. Resta vero che l'esposizione pubblica richiede TLS.

Il cookie ospite viene cancellato al login: a quel punto il carrello da ospite è già stato unito all'account e l'identificatore non serve più. Così la capability non resta nel browser per un anno e la navigazione dopo un eventuale logout riparte con un identificatore nuovo.

Il JWT è firmato, non cifrato. Il server accetta esclusivamente HS256, verifica firma, issuer, audience, intervallo temporale e subject, quindi risolve l'utente dal direttorio. Prezzi, stock, ruoli, nome ed e-mail non sono considerati affidabili dal cookie.

Difese implementate

  • segreto JWT di almeno 256 bit, fornito fuori dal WAR; in Production la sua assenza impedisce l'avvio dell'applicazione (una chiave casuale per JVM sloggherebbe tutti a ogni riavvio e farebbe rifiutare da ogni altro nodo i token emessi da questo), mentre in sviluppo resta un fallback casuale con warning in log;
  • cookie host-only (nessun Domain), path limitato all'app, HttpOnly, SameSite=Lax e Secure quando la richiesta è HTTPS;
  • validazione esatta Origin/Referer su POST, PUT, PATCH e DELETE, oltre a SameSite; assenza o mismatch producono 403;
  • bypass CSRF soltanto per Bearer token o protocollo API con header custom, non impostabile da una form cross-site; nessuna policy CORS viene concessa;
  • header nosniff, anti-framing, referrer policy e permissions policy;
  • identificatore guest con 256 bit di entropia e formato rigorosamente validato;
  • nessun HttpSession: in sviluppo la sua creazione accidentale interrompe la richiesta con lo stack trace del componente colpevole, in produzione viene registrata a livello SEVERE senza restituire un 500 al cliente;
  • pagina di diagnostica /status.xhtml raggiungibile anche in Production, per scelta: questa è una POC il cui interesse sta proprio nel mostrare su cosa gira (estensioni SIMD scelte da HotSpot, collector, memoria vista da un unikernel). È una decisione con un costo, quindi è esplicita: la pagina nomina host e JVM, e ogni proprietà o variabile d'ambiente il cui nome contenga password, secret, token, credential o key viene sostituita con [redacted] — verificato che ALIFACES_JWT_SECRET non compaia mai in chiaro. Un deployment che non sia un caso di studio la chiude con il context-param alifaces.DIAGNOSTICS=false, e allora risponde 404;
  • la chiave shopper mostrata nella striscia dimostrativa è troncata: il valore intero è quello del cookie guest, cioè una capability, e stamparlo per esteso vanificherebbe HttpOnly;
  • password demo memorizzate come PBKDF2, ma le password in chiaro restano nel sorgente per poterle mostrare: questa directory va sostituita in produzione;
  • lo storico ordini dimostrativo non viene seminato in Production, e fuori da Production viene saltato per gli account che ne hanno già uno — così un OrderStore condiviso non se lo ritrova moltiplicato per il numero di nodi.

Pagine cacheabili

index.xhtml e product.xhtml sono servite come guscio anonimo con Cache-Control: public, s-maxage=60 e nessun Set-Cookie: non contengono identità, quindi la stessa risposta può essere riusata per chiunque. Le parti personali arrivano da una chiamata successiva, che è una POST e quindi non cacheabile.

Due condizioni valgono come requisito di configurazione, non come dettaglio:

  • l'edge deve escludere i cookie di identità (alifacesAuth, alifacesShopper) dalla cache key — non servono al guscio — e includere alifacesLang e amaFacesTheme, che cambiano il markup;
  • nessun codice deve reintrodurre un Set-Cookie su quelle due pagine: sarebbe la via più rapida per far distribuire da una cache condivisa l'identificatore di un visitatore a tutti gli altri. Per questo ThemeSwitcher e LocaleSwitcher scrivono il cookie solo quando la preferenza arriva esplicitamente dall'URL.

/sitemap.xml e robots.txt sono pubblici per costruzione: elencano solo il catalogo, e robots.txt tiene i crawler fuori da carrello, login, ordini, API e pagina diagnostica.

/health/ready è pubblico e volutamente muto: risponde UP/DOWN sulla raggiungibilità dello store e non espone nulla dell'host, che resta materia di /status.xhtml (chiuso in Production).

Obblighi prima di una vera produzione

  • Terminare esclusivamente HTTPS, configurare HSTS all'edge e verificare il forwarding sicuro dello schema al container.
  • Impostare ALIFACES_JWT_SECRET in un secret manager e ALIFACES_PUBLIC_ORIGIN all'origine HTTPS canonica. Ruotare le chiavi; per più servizi preferire firme asimmetriche e kid.
  • Implementare access token brevi, refresh token con rotazione e revoca/denylist. Il logout attuale cancella il cookie ma non può invalidare un JWT già copiato.
  • Sostituire store in-memory e directory demo; garantire TTL, eviction, limiti di cardinalità, persistenza e atomicità del merge guest/account nel backend scelto.
  • Mettere rate limiting su login e API, WAF/edge limits, audit trail, metriche e alert.
  • Definire una Content Security Policy compatibile con PrimeFaces, eseguire SAST/SCA/DAST, penetration test e scansione delle immagini/container.
  • Modellare autorizzazioni per venditori, amministratori, rimborsi e accesso agli ordini; la POC mostra soltanto il flusso cliente.
  • Aggiungere pagamenti tokenizzati tramite provider certificato; non gestire carte nella POC.
  • Aggiornare PrimeFaces da una RC a una release stabile validata prima del go-live.

Segnalazioni

Non aprire pubblicamente dettagli di una vulnerabilità sfruttabile contro un'istanza attiva. Usare il canale privato indicato dal maintainer del deployment.

There aren't any published security advisories