Securitate JWT pentru Auth

Salutare,

Incerc sa-i dau de cap la authentificarea cu JWT. Nu gasesc nimic pe net care sa ma lamureasca. Poate ma puteti ajuta voi pentru o implementare fara 3rd parties.

Dupa signin punem in intr-un secure https cookie un JWT. Verificam acest JWT din cookie in backend pentru a autoriza userul.

Ce opreste pe cineva sa ia JWT-ul din cookies si sa-i faca brute force cat timp e necesar pentru a afla secretul ? Asta e partea care nu o inteleg. Si cum putem securiza ca acest lucru sa nu fie posibil ?

La autentificarea pe baza de sesiune e simplu. Daca sesiunea nu exista in cache, n-ai cum sa-i faci brute force. Iar in cel mai rau caz verificarea sa face pe backend si poti aplica un rate limiter.

La JWT il poti lua si poti face brute force direct pe propriul sistem. Nu ai cum sa combati decat daca rotesti cheia frecvent. Dar daca o rotesti, toti care au deja un JWT valid vor fi delogati.

Posibil sa-mi lipseasca ceva, voi cum faceti ?

Cum adică brute-force ?

Deci, dacă secretul tău pe servere este 1234secret, da e trivial de obținut. Dar dacă pui un secret la fel de lung ca hash-ul, e.g. de 256 biti nu îți va afla nimeni secret key-ul în următorii 100000 de ani. (Asta dacă nu a folosit cumva cineva exact același secret și acum e într-o bază de date)

2 Likes

Well, aici avem cateva aspecte…

  • Daca cineva are tokenul nu trebuie sa faca neaparat brute force ca poate face ce faci si tu cu el. Ca sa impiedici asta poti adauga un timp de expirare token (vezi exp claim aici).
  • Presupunand ca cineva ar face brute force, poti genera un secret pentru fiecare user combinat cu perioada de expirare ca sa minimizezi impactul in caz de reusita.
  • Daca cheia e foarte complexa e posibil sa dureze mai mult decriptarea si in general sa incetinesti conexiunile in backend pentru ca tokenul se transmite la fiecare request. De asemenea tokenul e bine sa fie cat mai scurt pentru ca orice adaugi acolo creste semnificativ payloadul.
  • Eu mai folosesc unde se poate politici de drepturi in token. De exemplu utilizatorii care doar consulta ceva au un token diferit de cei care administreaza ceva.
  • Mai poti folosi pentru criptare chei obtinute din alte surse cum ar fi de exemplu un API UUID online eventual combinat cu un salt local pe care nu il stochezi valabil pana la reboot.
2 Likes

Multumesc pentru raspunsuri !

Deci se merge pe idea ca dureaza foarte mult ca sa fie spart.

O solutie buna ca fiecare JWT sa aiba un secret dinamic astfel incat, chiar daca e spart unul, secretul nu o sa poata fi refolosit sa creeze noi tokenuri. Mersi de idee !

Cine impiedica atacatorul sa faca acelasi lucru cu certificatul ce-l folosesti pt HTTPS?

Ceritificatele folosesc sistemul de criptare cu chei publice (sau criptare asimetrica). Brute force se foloseste numai pentru a decripta chei simetrice, nu functioneaza in sistemele cu chei asimetrice.

2 Likes

Mai intai trebuie sa ia din device-ul utilizatorului token-ul, apoi sa identifice cheia de pe server.
Ca sa identifici cheia serverului cu brute force inseamna timp si permisiunea serverului.
Plus ca s-ar putea ca si daca gaseste cheia serverului, sa primeasca refuz din partea serverului pentru ca a expirat token-ul :slight_smile:

Mai mult, se poate ca serverul sa poata schimba token-ul la fiecare API request… ceea ce se practica de fapt.

In cazul in care te gandesti ca vrei sa interzici accesul unui utilizator fara sa mentii o baza de date cu token-uri invalidate inainte de data de expirare. Lucu care invalideaza beneficiile token-urilor JWT. Deoarece introduci un query intr-o baza de date la fiecare request (i.e. sesiuni).

Exista si conceptul de Refresh Tokens. In care ai doua token-uri. Unul care identifica utilizatorul cu un timp lung de expirare si unul care permite accesul utilizatorului cu un timp scurt de expirare (gen cateva minute).

Folosesti tokenul pentru access de fiecare data cand utilizatorul are nevoie de privilegii speciale si daca tokenul nu este valid (ex. a expirat) folosesti tokenul de identitate pentru a confirma ce utilizator este si daca utilizatorul inca are acces la aplicatie (probabil cauti in baza de date) si generezi alt token de access. Asta in cazul in care utilizatorul inca are acces la aplicatie.

Astfel, utilizatorul nu trebuie sa se logheze in aplicatie frecvent deoarece identitatea este cunoscuta datorita tokenul-ui de identitate. Si aplicatia poate sa genereze token-uri de acces fara cunostinta sau nevoia de interactie/confirmare a utilizatorului. Si tu esti sigur ca un acces special este limitat la un timp foarte scurt in care este inposibil sa obtii si sa faci ceva cu un token de access.

Cat despre brute-force. Aici trebuie sa gandesti altfel.

Este foarte corect ce spun colegii mai sus dar as preciza ca in functie de context trebuie vazut totusi cat de frecvent schimbi tokenul raportat la numarul de accesari pentru ca generarea si interpretarea lui costa daca ai foarte multe accesari in unitatea de timp. La fel se intampla daca legi procesul de autorizare cu token de o baza de date (preferabil ar fi sa nu faci asta). Eu de exemplu in ultima vreme am avut niste aplicatii de timp real unde backendul poate primi zeci de mii de request-uri pe concurente. In acest caz schimbarile de token frecvente sau un payload mai mare schimba dramatic situatia. Nu este neaparat acest caz un reper dar doar zic ca, daca este cazul, trebuie avut in vedere si acest aspect daca aplicatia sau API-ul este cu numar foarte mare de accesari intr-un timp scurt.

2 Likes

De ce n-ar functiona?

In cazul RSA de exemplu, spargerea consta in gasirea a doua numere prime ce inmultite dau numarul din cheia publica.

Exemplu: RSA numbers - Wikipedia

Acum brute-force aici nu inseamna sa incerci fiecare numar posibil, exista tehnici matematice mai avansate. Dar ramane faptul ca ai informatiile din cheia publica si atat, nu faci n apeluri catre server.

Brute force prin definitie inseamna incercarea unui numar mare de combinatii in scopul gasirii uneia potrivite. Orice alt algoritm nu mai este brute force. De aceea aceasta metoda poate fi folosita in fortarea cheilor simetrice unde aceeasi combinatie cripteaza si decripteaza ceva (un passphrase). Aici de exemplu conteaza si lungimea cheii care intr-o dimensiune mai mare nu mai face posibila decriptarea cu resursele actuale intr-o unitate de timp decenta. De fapt pe asta se bazeaza toata treaba si de aceea periodic se incrementeaza lungimea cheilor odata cu avansul tehnologic. In cazul cheilor asimetrice unde cheia privata e cantitativ mult mai mare decat o parola e practic imposibil chiar si cu resurse mari sa ghicesti combinatia unei chei private care sa decripteze intr-un timp rezonabil ce s-a criptat cu cheia publica. Din acest motiv sistemul cu cheie publica este inca unul sigur si nu avem surprize cand folosim certificate.

Să mai adaug și eu ceva idei pe lângă multe altele foarte bune zise mai sus pe care nu le mai repet (expiry date mic pentru access tokens în format Jwt astfel încât brute force să fie infezabil, sau folosește algoritmi cu cheie public-privată RSA/EDSA sau HMAC cu cheie potrivită).

În primul rând e amintită authentificarea cu JWT. Oare te referi la access tokens? Un access token conform specificației Oauth2 nu trebuie neapărat să fie JWT. Poate fi orice string, chiar și un UUID. Am lucrat în sisteme în care access tokenul e un String (se mai numește și token opac pentru că nu are claims în el, seamănă mai mult cu un cookie de sessionid). Acolo era implementat conceptul de API gateway care schimba acel access token bazat pe String cu un JWT token prin back-channel după ce își făcea treaba de gateway/WAF (request sanitization, filtering by various rules, blocking paths, rate limiting, IP filtering, etc etc) care putea fi folosit mai departe pentru comunicarea internă între microserviciile dinăutrul rețelei private.

Dacă în schimb folosești Open ID Connect (scope: openid / response_type: id_token token) ca să primești și un id_token, nu ar trebui să folosești acel token pentru a face API calls, id tokenul ar trebuie folosit doar pentru frontend pentru a customiza interfața pentru acel user, și doar access tokenul pentru API calls.

Bun și pentru access tokens în format JWT s-a discutat mai sus cum poate fi securizat anti brute-force :slight_smile:

Ah one more thing: am văzut recent că un pattern foarte bun de a elimina complet orice fel de problem cu flowurile de oauth2 e să se renunțe complet la oauth pe partea de FE și să ai un backend for frontend (BFF) care e un proxy prin care aplicația ta comunică cu restul sistemului. FE-ul folosește session cookies pentru a comunica cu BFF secured și BFF se ocupă de Oauth. Adică face flowul de Authorization Code Grant cu client privat, adică să folosești client_secret sau signed JWT. Diferența când face un BFF sau un FE e că FE nu poate folosi clientul privat, doar un client public pentru că nu poate păstra secretul. Și așa ai redus extrem de mult suprafața de atac.

And one more thing, am văzut multe tentative de hacking de-a lungul a ultimilor ani pe sisteme de autentificare în diverse moduri foarte creative, dar niciodată nu a încercat nimeni să facă brute force-uri de genul ăsta pentru că pur și simplu e infezabil cu un minim de bune practice deja amintite în primul paragraf.