Uvod
Budući da smo u jednom od prethodnih članaka detaljno predstavili postupak izrade formulara za autentifikaciju korisnika, čini se da je upoznavanje sa sistemima za autorizaciju korisnika, sledeći prirodan korak.
Pre svega, objasnićemo u čemu je zapravo razlika između dva navedena pojma:
- autentifikacija je proces utvrđivanja identiteta korisnika (proverava se da li korisnik uopšte ima pravo pristupa sistemu)
- autorizacija podrazumeva definisanje nivoa privilegija za korisnike koji su prethodno stekli pravo pristupa
Korišćenje JSON Web Tokena predstavlja jedan od načina za autorizaciju korisnika i glavnu temu članka, ali, prvo ćemo se ponešto detaljnije osvrnuti na problematiku autorizacije u opštem smislu.
Problematika autorizacije korisnika uopšteno
Postupak autorizacije naizgled ne deluje previše komplikovano i stoga većina korisnika (i izvestan broj nedovoljno iskusnih programera), često ima utisak da posle uspešno završene prijave (to jest, posle uspešno obavljene autentifikacije), nema potrebe za preduzimanjem dodatnih koraka u situacijama kada se korisnik nadalje obraća serveru, ili (malo drugačije), deluje da se dalja komunikacija između korisnika i servera (u smislu provere identiteta korisnika i utvrđivanja privilegija), odvija "sama od sebe".
Naravno, takav utisak je (u najmanju ruku) varljiv, i svakako postoji razlika između toga šta se prividno dešava, i onoga što se zapravo dešava pri prosleđivanju podataka ....
Šta se naizgled dešava posle prijave korisnika?
Jednostavan odgovor na pitanje iz naslova mogao bi biti: "Posle prijave, korisnik dobija pravo pristupa odgovarajućim sadržajima".
Koristeći se (svima dobro poznatim) primerom društvenih mreža, posle prijave na nalog (autentifikacija), korisnik dobija pristup početnoj stranici na kojoj (najčešće u gornjem delu prozora), može videti svoju sliku (avatar) i korisničko ime, dok centralni deo zauzimaju sadržaji koje su objavljivali korisnik i osobe sa kojima je korisnik u kontaktu.
Ako korisnik pozove chat aplikaciju, može nekome od prijatelja poslati poruku pod svojim imenom i, takođe, sledeći put kada otvori istu stranicu u browseru, neće morati ponovo da se prijavljuje, dočekaće ga isti sadržaji (zapravo, najverovatnije ponešto izmenjeni sadržaji - u međuvremenu će još neko od poznanika napraviti još neku objavu), i naravno - niko neće moći tek tako da "čeprka" po nalogu korisnika ....
Šta se zapravo dešava posle prijave korisnika?
Na drugo pitanje daćemo drugačiji odgovor: na površini se i dalje dešava sve ono što smo prethodno opisali, ali (pretpostavljamo da je do sada postalo sasvim jasno) - ne dešava se "samo od sebe".
Da bi sve, iz perspektive korisnika, u praksi funkcionisalo onako kako smo navikli (i naveli u prethodnom odeljku), u pozadini se odvijaju brojni procesi.
Pre svega, browser sam po sebi ne pamti uneto korisničko ime i lozinku automatski (što teoretski znači da bi pri sledećem pokretanju trebalo ponovo uneti navedene podatke).
Teoretski, mogli bismo svaki put da unosimo podatke za prijavu, ali, u praksi (s obzirom na to da je vrlo nepraktično svaki put unositi podatke za prijavu), jasno je da se "podaci za prijavu" moraju "nekako" čuvati (koristimo navodnike, jer sistem neće čuvati podatke za prijavu u izvornom obliku (bar ne lozinku)).
Pored do sada navedenih detalja, može se desiti i da korisnik izgubi privilegije za pristup određenoj aplikaciji (recimo, privilegije za slanje poruka u chat aplikaciji, zbog neprimerenog ponašanja i sl), pri čemu je aplikacija ostala otvorena, što, ukoliko se privilegije ne proveravaju pri slanju svakog zahteva, može dovesti do nepravilnosti u radu.
Dakle, pri slanju svakog zahteva potrebno je obaviti autorizaciju, što podrazumeva jednu od dve mogućnosti:
- prosleđivanje podataka za prijavu (korisničko ime i lozinka)
- prosleđivanje posebno formatiranih podataka za autorizaciju, koji se čuvaju u nekom od lokalnih skladišta
Pošto znamo da nećemo (u većini slučajeva), prosleđivati korisničko ime i lozinku uz svaki zahtev (to jest, u većini situacija je znatno praktičnije izabrati opciju #2), vraćamo se na pitanje: koje podatke je potrebno čuvati i - u kom formatu?
Reklo bi se da je došao trenutak da u diskusiju vratimo JSON web tokene ....
JSON Web Tokeni - Struktura, oblast primene i postupak autorizacije
U spoljnjem svetu, reč token označava "žeton"; u bukvalnom smislu - pljosnati komad plastike ili metala, (najčešće) okruglog oblika, koji se u ranijim vremenima koristio za javne telefone (ili, u zabavnim parkovima, za pristup električnim automobilima, ringišpilima i sl).
U smislu pojave, opisani objekat je samo oblikovani komad plastike ili metala, ali, u smislu značenja koje ima u određenom kontekstu, žeton (simbolično) predstavlja privilegiju osobe koja nosi žeton, da se služi određenim sadržajima.
Kao što žetoni u zabavnim parkovima omogućavaju posetiocima da koriste sadržaje bez daljeg plaćanja novcem (posle kupovine žetona), tako i web tokeni simbolično predstavljaju privilegiju korisnika da pristupi određenim sadržajima web aplikacije, odnosno, da koristi aplikaciju/sajt na određen način - bez potrebe da "svaki čas" prosleđuje osetljive podatke koje je koristio za prijavu (korisničko ime i lozinku).
Da bi web token u praksi funkcionisao kao mehanizam za autorizaciju, mora se postići ravnoteža između dva oprečna zahteva: JSON web token treba da sadrži dovoljno informacija za identifikaciju korisnika i dodelu privilegija, ali - tako da nijedna od zapisanih informacija ne bude poverljiva (lozinka ili drugi osetljivi podaci).
Prvo ćemo detaljno razmotriti sve zahteve koji se postavljaju pred ovakav sistem za autorizaciju, a onda ćemo se osvrnuti i na implementaciju.
Struktura JSON Web Tokena
Zahtevi koji se stavljaju pred web token su sledeći:
- token mora biti u stanju da identifikuje korisnika i omogući dodelu odgovarajućih privilegija korisniku
- navedeni zadatak potrebno je rešiti bez čuvanja osetljivih podataka o korisniku (ime, prezime, pogotovo ne korisnička lozinka i drugi osetljivi podaci)
- token obavezno mora sadržati određeni mehanizam za zaštitu podataka (kao zamenu za lozinku)
- svi podaci koji su potrebni za autorizaciju, moraju biti sadržani u samom tokenu, i treba ih čuvati na strani klijenta
Implementacija koja poštuje sve navedene zahteve, može se realizovati preko JSON objekta koji se sastoji iz tri dela:
- zaglavlje - deo u kome je naveden kriptografski algoritam preko koga nastaje potpis (poslednji deo JWT-a)
- sadržaj - glavni deo tokena, koji nosi informacije za autorizaciju (korisničko ime, nivo privilegija i druge informacije koje su neophodne za autorizaciju, ali, nisu poverljive)
- potpis - kontrolni deo koji nastaje enkripcijom kombinacije prethodna dva dela, uz korišćenje sigurnosne šifre (nije u pitanju korisnička lozinka, već posebna šifra koja se čuva na serveru)
Prva dva dela JSON Web Tokena koji odgovara navedenim zahtevima, biće enkodirani, a pre enkodiranja, navedeni delovi imaju strukturu koja je definisana po sledećem obrascu:
Potpis nastaje kriptovanjem enkodiranog zaglavlja i sadržaja, uz korišćenje šifre:
Posle završenog procesa enkodiranja, token dobija svoj finalni oblik:
Da pojasnimo dodatno: enkodiranje koje smo spomenuli (Base64) nije metoda enkripcije (odnosno, metoda zaštite), već, način da se određeni niz bajtova - pre enkripcije - svede na nisku koja odgovara određenoj specifikaciji (u konkretnom slučaju, proizvoljna niska znakova koja možda sadrži i UNICODE znake, biva svedena na nisku koja se sastoji isključivo iz ASCII slova i cifara).
Enkodiranje, kao proces koji je reverzibilan, * ne pruža nikakvu zaštitu (odnosno "skrivanje") podataka.
Zaglavlje i sadržaj su direktno dostupni bilo kome ko pročita token, ali - to je sasvim u redu, budući da smo još ranije utvrdili da JWT neće nositi poverljive podatke (već samo podatke koji su i inače javno dostupni), i stoga ne moramo brinuti o tome da će preko JW tokena neko doći do poverljivih podataka.
Zaštita se postiže upotrebom hash funkcija (najčešće je u pitanju algoritam SHA256), uz korišćenje tajne šifre na serveru ("secret").
Enkripcija je postupak svođenja niza bajtova na nisku proizvoljne dužine (SHA256 vraća niske dužine 64 karaktera), čiji sadržaj se ne može iskoristiti za dobijanje ulaznog podatka (u ovom slučaju, proces nije reverzibilan), ali, u pitanju je niska koja je jedinstvena (teorija nalaže da bi svakom ulaznom podatku morala odgovarati samo jedna niska koja nastaje procesom haširanja, a kada je u pitanju algoritma SHA256, praksa još uvek nije opovrgla teoriju).
Generator JWT-a
Ako želite da isprobate kako u praksi funkcioniše kreiranje JSON web tokena, sa podacima po vašem izboru, možete iskoristiti donji formular.
{ "alg": "HS256", "typ": "JWT" }Sadržaj:
{ "id": 411, "admin": false, "korisnicko_ime": "darth_korisnik" }Lozinka:
You .... shall .... not .... pass!Token:
Primećujemo da se enkodirana verzija središnje niske menja samo "mestimično" (u onom delu koji proporcionalno odgovara mestu na kom smo napravili izmenu u ulaznoj niski), dok se potpis drastično menja pri svakoj (pa i najmanjoj) promeni sadržaja.
Upravo tako i treba da bude (i to jeste razlika između enkodiranja i haširanja).
Postupak autorizacije korisnika preko JWT-a
Budući da se šifra za enkripciju potpisa čuva na serveru, * i budući da se korisnicima (posle uspešne autentifikacije) šalje 'potpis' koji nastaje korišćenjem šifre, za utvrđivanje autentičnosti zahteva (koji klijent kasnije šalje serveru), dovoljno je proveriti samo token koji server prima zajedno sa zahtevom.
Sadržaj tokena (koji, između ostalog, čine podaci za autorizaciju i potpis), jeste moguće izmeniti, ali - bez lozinke sa servera - praktično je nemoguće napraviti potpis koji odgovara zaglavlju i podacima za autorizaciju.
Kada browser pošalje serveru token (pri slanju zahteva za pristup korisničkoj stranici, slanju chat poruke i sl), server obavlja sledeće radnje:
- prima token
- čita zaglavlje i sadržaj
- kreira potpis shodno primljenom zaglavlju i sadržaju
- upoređuje kreirani potpis sa potpisom koji je došao uz token
- ukoliko se potpisi poklapaju - korisniku se dozvoljava pristup sadržajima
- ukoliko se potpisi ne poklapaju - korisnik gubi privilegije za pristup
Primeri korišćenja JSON Web Tokena
Uz nekoliko slika (jer "slika vredi ~1024 reči"), lakše ćemo sagledati kako sistem zaštite funkcioniše u praksi ....
Uspešna autentifikacija
Kada korisnik prosledi korektne podatke za prijavu ....
.... na serveru se kreira token koji sadrži zaglavlje, podatke i potpis ....
.... i potom se token šalje klijentu ....
Po prijemu tokena, klijent dobija pristup aplikaciji.
Neuspešna autentifikacija
Ako korisnik ne prosledi odgovarajuće podatke za prijavu ....
.... token neće ni biti kreiran.
Za ovakve situacije se mogu implementirati rešenja koja dozvoljavaju samo određen broj pokušaja (pristupa), pre nego što se formular za pristup privremeno zablokira (na primer, 15 minuta pauze posle 5. neuspešnog pokušaja i sl), a može se koristiti i "captcha" ili neki sličan mehanizam.
Uspešna autorizacija
U sledećoj situaciji, korisnik pred sobom već ima otvorenu aplikaciju (sa sadržajima koji odgovaraju korisničkom nalogu). Pri slanju novog zahteva, potrebno je utvrditi da li korisnik (i dalje) ima privilegije za pristup sadržajima na serveru.
Ako za primer uzmemo slanje poruke u chat aplikaciji, provera funkcioniše na sledeći način:
- klijent serveru prosleđuje zahtev za obradu podataka
- ukoliko zahtev bude prihvaćen, poslata poruka će biti dodata u bazu podataka i prikazana sa ostalim porukama
- ako zahtev bude odbijen, sistem neće uneti novu poruku u bazu, * a klijentu koji je poruku poslao, biće prosleđena poruka o grešci
- uz zahtev se prosleđuje i JSON web token.
Token se proverava po pravilima koja smo prethodno naveli (da li potpis koji je poslat uz token odgovara sadržaju) ....
.... i pošto u ovom slučaju provera daje korektan rezultat ....
.... zahtev (poruka) je prihvaćen(a):
U sledećem ciklusu ažuriranja prikaza u chat aplikaciji, nova poruka (zajedno sa prethodnim porukama), biće prikazana svim klijentima koji imaju privilegije da vide poruke.
Neuspešna autorizacija
Ako neko pokuša da "prošvercuje" token sa izmenjenim sadržajem (recimo, običan korisnik pokušava da se predstavi kao administrator) ....
.... sadržaj tokena će sam po sebi "delovati" dobro, ali, pošto poslati potpis nije odgovarajući ....
.... provera neće vratiti korektan rezultat.
Token se stoga ne prihvata, samim tim ni poruka neće biti prihvaćena (!) ....
.... a korisnik koji je pokušao da prosledi poruku uz nevažeći token, biće obavešten o tome da je došlo do greške (ostali korisnici, naravno, neće bez potrebe videti poruku upozorenja).
Zaključak
Korišćenje JSON Web Tokena, uglavnom predstavlja sasvim pouzdan sistem za autorizaciju (koji je takođe i veoma popularan u poslednjih desetak godina, otkako se pojavio), ali, JSON tokeni nisu "svemogući i nepogrešivi".
U svemu što smo pisali do sada, podrazumevalo se da server "veruje" tokenu, međutim (kao što smo takođe naveli), tokeni mogu biti i kompromitovani (ne mogu se "obijati" tek tako, ali, nije ni nemoguće), i stoga ćemo temi sigurnosti tokena (o tome kako je najbolje skladištiti ih i sl), temama koje su vezane za enkodiranje i enkripciju, kao i ostalim temama koje se tiču autorizacije - uskoro posvetiti mnogo više pažnje ....