JWT vs Sesiune: ce mecanism de autentificare să alegi?
A autentifica un utilizator înseamnă a ști, la fiecare cerere, cine este. Se confruntă două mari familii: sesiunile de server, în care serverul păstrează evidența utilizatorului conectat, și JSON Web Tokens (JWT), în care identitatea călătorește într-un token semnat purtat de client. Alegerea influențează securitatea, capacitatea de a scala și ușurința de a deconecta un utilizator. Iată cum să decizi în funcție de contextul tău.
Sesiunile de server (stateful)
Cu o sesiune, serverul creează un identificator de sesiune la conectare, stochează datele asociate (identitate, roluri, coș) pe partea serverului (memorie, Redis, bază de date) și returnează browserului un cookie care conține doar acest identificator. La fiecare cerere, serverul citește cookie-ul, regăsește sesiunea și știe cine vorbește.
- Stare pe partea serverului: sursa de adevăr rămâne pe server, clientul poartă doar o referință opacă.
- Revocare imediată: ștergerea sesiunii pe partea serverului deconectează instantaneu utilizatorul.
- Cookie: transmis automat de browser, ideal în
HttpOnly,SecureșiSameSite.
JSON Web Tokens (stateless)
Un JWT este un token autonom compus din trei părți codificate în base64url și separate prin puncte: un header, un payload (claim-urile, de exemplu identificatorul utilizatorului și expirarea) și o semnătură. Serverul semnează token-ul la conectare; apoi îi este suficient să verifice semnătura pentru a avea încredere în conținut, fără a stoca nimic.
- Fără stare: toată informația necesară se află în token, serverul nu are nevoie de memorie partajată.
- Verificabil oriunde: orice serviciu care cunoaște cheia poate valida token-ul, practic pentru arhitecturile distribuite și SSO.
- Instrumente: poți inspecta un token cu decodorul JWT, controla semnătura sa cu verificatorul JWT sau crea unul cu generatorul JWT.
Tabel comparativ
| Criteriu | Sesiune de server | JWT |
|---|---|---|
| Stare | Stateful (stocat pe server) | Stateless (purtat de client) |
| Stocare pe server | Necesară (Redis, bază de date) | Niciuna |
| Revocare | Imediată | Dificilă înainte de expirare |
| Scalabilitate orizontală | Store partajat necesar | Nativă |
| Dimensiune transmisă | Mică (un identificator) | Mai mare (claim-uri semnate) |
| Inter-domeniu / SSO | Restrictiv | Adaptat |
| Suprafață XSS | Redusă dacă cookie HttpOnly | Ridicată dacă stocat în localStorage |
Securitate: XSS, CSRF și revocare
Ambele abordări sunt sigure dacă sunt bine implementate, dar riscurile lor diferă.
- XSS: un cookie de sesiune
HttpOnlyeste inaccesibil JavaScript-ului, deci protejat de furt prin injecție. Un JWT stocat înlocalStorageeste, în schimb, citibil de orice script, ceea ce îl face o țintă predilectă. Stocarea JWT-ului într-un cookieHttpOnlyanulează acest avantaj al JWT-ului dar reintroduce riscul CSRF. - CSRF: cookie-urile sunt trimise automat, deci vulnerabile la CSRF fără protecție (atributul
SameSite, token anti-CSRF). Un JWT trimis manual în antetulAuthorizationnu este afectat. - Revocare: este punctul slab al JWT-ului. Fiind autonom, nu poate fi invalidat înainte de expirarea sa fără a reintroduce o stare de server (listă de revocare, blacklist). O sesiune se șterge instantaneu.
Scalabilitate și arhitectură
Pe un singur server, sesiunile sunt triviale. Imediat ce distribui sarcina pe mai multe instanțe, fiecare instanță trebuie să acceseze sesiunile: este nevoie de un store partajat (Redis) sau de sticky sessions. JWT-ul strălucește aici, deoarece orice instanță validează token-ul fără apel de rețea și fără stocare comună.
- Microservicii: un JWT propagă identitatea de la un serviciu la altul fără bază centralizată.
- API-uri publice și mobile: JWT-ul evită gestionarea cookie-urilor pe partea clientului nativ.
- Monolit clasic: sesiunea rămâne mai simplă și mai sigură în mod implicit.
Când să alegi unul sau altul
Alege sesiunile când
- Dezvolți o aplicație web clasică cu randare pe server
- Revocarea imediată este critică (bancă, sănătate, back-office)
- Vrei soluția cea mai sigură în mod implicit, cu cele mai puține capcane
- Infrastructura ta suportă un store de sesiuni partajat fără dificultate
Alege JWT-ul când
- Expui un API consumat de SPA-uri, mobil sau terți
- Ai o arhitectură de microservicii sau SSO între domenii
- Trebuie să scalezi orizontal fără store partajat
- Accepți să gestionezi expirarea scurtă și reîmprospătarea token-urilor
Recomandare
Pentru majoritatea aplicațiilor web, sesiunile de server rămân alegerea cea mai sigură și cea mai simplă: revocare imediată, cookie HttpOnly și zero gestionare de token pe partea clientului. Rezervă JWT-ul pentru cazurile în care absența stării sale aduce o valoare reală: API stateless, mobil, microservicii, SSO.
Dacă optezi pentru JWT, păstrează o durată de viață scurtă (câteva minute) cuplată cu un refresh token stocat într-un cookie HttpOnly, și prevezi o listă de revocare pentru cazurile sensibile. Combini astfel ce e mai bun din ambele lumi.
Întrebări frecvente
Un JWT este criptat?
Nu, în mod implicit un JWT este doar semnat, nu criptat. Payload-ul său este codificat în base64url și citibil de oricine îl interceptează. Nu pune niciodată date sensibile în clar într-un JWT. Pentru a cripta conținutul, trebuie să recurgi la JWE (JSON Web Encryption).
Unde să stochezi un JWT pe partea clientului?
Cel mai sigur este un cookie HttpOnly, Secure și SameSite, care protejează de furt prin XSS. localStorage este mai simplu dar expune token-ul oricărui script malițios. Evită-l pentru token-uri cu privilegii ridicate.
Cum deconectezi un utilizator cu un JWT?
Fiind token-ul autonom, deconectarea reală necesită fie așteptarea expirării sale, fie menținerea unei liste de revocare pe partea serverului. De aceea se folosesc durate de viață scurte și un refresh token care, acela, poate fi revocat.
Se pot combina sesiunile și JWT-ul?
Da, este o practică obișnuită: un access token JWT cu durată de viață scurtă pentru apelurile API, și un refresh token gestionat ca o sesiune (stocat și revocabil pe partea serverului) pentru reînnoirea access token-ului.