Kako implementirati JWT u API-jima izgrađenim pomoću Node.js-a

  • Razumijevanje strukture JWT-a (zaglavlje, korisni teret i potpis) i njegove uloge u autentifikaciji bez stanja.
  • Konfigurišite API u Node.js i Expressu sa middleware-om koji verifikuje i štiti rute koristeći JWT tokene.
  • Primjenjujte dobre sigurnosne prakse: korištenje HTTPS-a, upravljanje tajnim podacima, kratki rok trajanja, tokene za osvježavanje i opoziv.
  • Integrirajte eksternu autentifikaciju kao što je Google OAuth naknadnim izdavanjem vlastitog JWT-a za API.

Implementacija JWT-a u API-jima izgrađenim pomoću Node.js-a

Kada počnete graditi API-je s Node.js-om i morate se pozabaviti sigurnosnim aspektom, prije ili kasnije se pojavljuje isti ključni igrač: JSON Web Tokens (JWT) kao sistem autentifikacije . Oni su lagani, odlično rade s modernim arhitekturama i omogućavaju vam skaliranje bez pretjeranog upravljanja serverskim sesijama.

U ovom vodiču ćete korak po korak i detaljno vidjeti kako JWT interno funkcioniše i kako ga integrirati u Node.js API s Expressom . Obuhvata standardnu ​​prijavu korisničkim imenom i lozinkom i prijavu na Google, kao i najbolje prakse za sigurnost, opoziv, korištenje HTTPS-a, middleware i strukturu projekta. Cilj je da do kraja čitanja steknete sveobuhvatno razumijevanje i budete u mogućnosti da ga implementirate u vlastiti API bez ikakvih iznenađenja.

Šta je JWT i kako je strukturiran token?

JSON web token je zvanično otvoreni standard (RFC 7519) za prenos zahtjeva u JSON formatu između dvije strane na kompaktan način. U praktičnom smislu, to je jednostavan tekstualni niz sa tri Base64 bloka odvojena tačkama: zaglavlje, korisni teret i potpis.

Kada se korisnik uspješno registruje ili prijavi na vaš API, backend generiše JWT token koji sadrži njegove podatke i vraća ga klijentu. Ovaj token se zatim uključuje u svaki sljedeći zahtjev , obično unutar zaglavlja autorizacije kao nosilac, kako bi se provjerio identitet i dozvole korisnika.

Tipičan primjer tokena bi izgledao ovako: zaglavlje kodirano u Base64, korisni teret kodiran u Base64 i potpis izračunat pomoću hash algoritma , sve povezano tačkama. Iako se može činiti besmislenim, zapravo je vrlo sistematičan format.

Ako uzmete taj string i zalijepite ga u alate poput jwt.io , vidjet ćete kako se dekodira u hodu: s jedne strane je sirovi token, a s druge strane, u običnom tekstu, zaglavlje i korisni teret, s ključevima i vrijednostima koje ste unijeli prilikom generiranja.

Zaglavlje obično uključuje polja kao što su alg (algoritam, na primjer, HS256) i typ (tip tokena, obično JWT). Korisni teret sadrži tvrdnje, odnosno informacije koje želite prenijeti: korisnički ID, ime, ulogu, datum isteka itd. Potpis se izračunava primjenom HMAC-a ili algoritma potpisa na kodirano zaglavlje i korisni teret, zajedno sa tajnim ključem ili parom javnog/privatnog ključa.

Najčešći mehanizam potpisivanja u Node.js i Express API-jima je HMAC-SHA256 (HS256) , koji kombinuje kod za autentifikaciju poruke zasnovan na hešu sa dijeljenim tajnim podatom. Potpis se generiše primjenom funkcije na: zaglavlje kodirano u Base64, korisni teret kodiran u Base64 i tajni podatak poznat samo serveru . Pored toga, preporučljivo je razumjeti kako upravljati certifikatima i potpisima pri radu sa javnim/privatnim ključevima.

Ključno je to što, ako neko usput mijenja token, potpis se više neće podudarati kada ga server provjeri . Čim provjera potpisa ne uspije, backend zna da je token modificiran ili da ne dolazi iz legitimnog izvora, a zahtjev bi trebao biti odbijen s greškom 401 ili 403.

Važno je shvatiti da Base64 nije enkripcija, već samo kodiranje . Sve što stavite u korisni teret može se pročitati ako neko uhvati token, zbog čega ne biste trebali uključivati ​​kritične informacije u običnom tekstu unutar JWT-a. Zato je također obavezno kombinirati JWT s HTTPS-om , tako da sav promet putuje šifriran i da se ne može lako pretraživati ​​preko mreže.

Prednosti korištenja JWT-a u Node.js API-jima

Jedan od glavnih razloga zašto je JWT postao toliko popularan je taj što su tokeni samostalni i bez statusa . To znači da, za validaciju korisnika, server ne mora pristupati bazi podataka sa svakim zahtjevom, budući da su relevantne informacije sadržane u potpisanom tokenu.

Budući da je to jednostavno niz teksta, JWT se može slati u URL-ovima, u tijelu POST zahtjeva ili u HTTP zaglavljima , besprijekorno se uklapajući u tipične REST API i SPA (Single Page Application) tokove. To je kompaktan format, idealan za mobilne aplikacije, frontendove u Reactu, Vueu, Angularu itd., pa čak i za komunikaciju između mikroservisa.

Još jedna jaka strana je fleksibilnost algoritama za potpisivanje: možete koristiti HMAC sa dijeljenim tajnim kodom (HS256) ili se odlučiti za asimetrične sheme kao što su RSA ili ECDSA, gdje potpisujete privatnim ključem, a provjeravate javnim ključem, što je vrlo korisno u složenim poslovnim okruženjima.

U arhitekturama s više servisa, JWT je odličan izbor jer omogućava različitim mikroservisima da vjeruju istom tokenu bez potrebe za dijeljenjem sesija u memoriji ili preraspodjelom korisničkog stanja. Svaki servis treba samo ključ za provjeru i pravila za interpretaciju zahtjeva.

Sa UX perspektive, ako se pravilno implementira, JWT omogućava glatke i skalabilne sesije , podržavajući mehanizme osvježavanja tokena, kontrolirane isteke i, općenito, dobru ravnotežu između sigurnosti i praktičnosti za korisnike.

Pripremite razvojno okruženje pomoću Node.js-a i Express-a

Za postavljanje API-ja s JWT autentifikacijom u Node.js-u, uobičajeni pristup je korištenje Expressa kao HTTP okvira i njegova kombinacija s nekoliko ključnih biblioteka koje znatno pojednostavljuju stvari.

Tipičan početni tijek rada bio bi kreiranje nove mape za projekat, ulazak u nju i pokretanje `npm init -y` za generiranje datoteke package.json. Odatle instalirate potrebne zavisnosti: Express za server, jsonwebtoken za rukovanje JWT-om, bcrypt ili bcryptjs za heširanje lozinki i dotenv za upravljanje varijablama okruženja.

Ako ćete raditi sa stvarnom bazom podataka, kao što je MongoDB, Mongoose se obično koristi za definiranje modela i shema . U tom slučaju, direktorij vašeg projekta će imati nešto poput ovoga: glavnu datoteku za pokretanje (na primjer, app.js), mapu models s modelom User.js i .env datoteku s osjetljivim ključevima.

Unutar .env datoteke možete pohraniti stvari poput JWT_SECRET, URI-ja veze s bazom podataka i vremena isteka tokena . Zatim ćete učitati ove podatke pomoću dotenv-a kako ne bi bili čvrsto kodirani u izvornom kodu ili preneseni u repozitorije.

Korisnički model obično uključuje polja kao što su korisničko ime ili e-pošta i lozinka , te hook za prethodno spremanje koji hešira lozinku prije pohranjivanja. Na ovaj način, čak i ako neko pristupi bazi podataka, neće imati lozinke u običnom tekstu, već hešove generirane pomoću bcrypt-a.

U glavnoj datoteci aplikacije, uobičajena praksa je inicijaliziranje Expressa, aktiviranje middleware-a za parsiranje JSON-a , povezivanje s MongoDB-om (ako ga koristite) i definiranje ruta za registraciju, prijavu i pristup zaštićenim resursima. Nakon toga slijedi ključni dio: generiranje i verifikacija JWT tokena.

Kompletan tok autentifikacije sa JWT-om

U JWT-baziranom sistemu autentifikacije, tipičan redoslijed obično uključuje registraciju korisnika, prijavu, izdavanje tokena, verifikaciju svakog zahtjeva i zaštitu privatnih ruta . Iako se detalji mogu razlikovati, ukupna struktura je uvijek ista.

Tokom registracije , kada korisnik pošalje svoje podatke od klijenta, vaš API prima tijelo zahtjeva, hešira lozinku pomoću bcrypt-a, sprema korisnika u bazu podataka (ili u strukturu u memoriji ako radite demo) i odgovara potvrdom registracije. Vraćanje tokena tokom registracije nije obavezno, iako mnogi API-ji to rade kako bi izbjegli dodatni korak prijave.

Stvari postaju zanimljive tokom prijave : primate korisničko ime i lozinku, pretražujete korisnika u bazi podataka, upoređujete unetu lozinku sa pohranjenim hashom koristeći bcrypt i, ako se podudara, generišete JWT token koristeći funkciju `sign` iz biblioteke jsonwebtoken . Ovaj token uključuje korisne tvrdnje: ID korisnika, ime, ulogu i, što je vrlo važno, datum isteka.

Prilikom konfigurisanja tokena, dobra je praksa postaviti razumno vrijeme isteka, kao što je 1 ili 2 sata za pristupni token . Ovo smanjuje vrijeme izloženosti ako token završi u pogrešnim rukama. Za duže sesije, dolazi do izražaja upotreba tokena za osvježavanje, o čemu ćemo kasnije razgovarati.

Tajni ključ koji koristite za potpisivanje tokena treba da potiče iz varijable okruženja, nikada ne smije biti direktno napisan u kodu . Ako sumnjate da je ovaj ključ u bilo kojem trenutku kompromitovan, vaša sigurnosna strategija treba da uključuje rotaciju tajnih ključeva, opoziv starih tokena i generisanje novih.

Nakon što prijava vrati token klijentu, klijent ga pohranjuje (na primjer, u httpOnly kolačić ili u memoriju) i šalje ga u zaglavlju autorizacije sa shemom nosača pri svakom pozivu zaštićenim krajnjim tačkama. Server će, na tim krajnjim tačkama, aktivirati middleware za validaciju tokena prije nego što nastavi s logikom kontrolera.

JWT verifikacija i middleware za zaštitu rute

Jezgro autentifikacije u Node.js i Express API-ju je obično višekratno upotrebljiv middleware koji provjerava token na svakom zahtjevu . Fizički, to je funkcija koja se izvršava prije obrađivača rute i odlučuje da li zahtjev prolazi filter ili je odbijen.

Ovaj middleware može tražiti token na različitim mjestima, ali najčešće čita zaglavlje autorizacije u formatu Bearer <token> . U drugim projektima, vidjet ćete prilagođeno zaglavlje poput x-auth-token; koncept je isti, mijenja se samo naziv zaglavlja.

Nakon što se token preuzme, middleware poziva ` jwt.verify` s tokenom i tajnim ključem . Ako se potpis podudara i token nije istekao, dobit ćete dekodirani korisni teret. Ovaj korisni teret je obično priložen objektu `req` (na primjer, `req.user`) tako da drugi kontroleri znaju ko je korisnik i koje zahtjeve ima.

Ako verifikacija ne uspije, bilo zato što se potpis ne podudara, token je modificiran, istekao je ili jednostavno ne postoji, odgovarajući odgovor middleware-a bit će 401 (nije autentificirano) ili 403 (zabranjeno) , ovisno o tome je li problem nedostatak vjerodajnica ili nedovoljne dozvole.

Zahvaljujući ovom pristupu, možete lako zaštititi svoje privatne krajnje tačke: jednostavno umetnite middleware kao drugi argument u definiciji rute . Na primjer, u ruti /clients koja vraća podatke o kupcima, možete postaviti auth middleware kao drugi parametar, tako da ako zahtjev ne sadrži važeći token, informacije se ne vraćaju.

Ako pokušate poslati zahtjev zaštićenoj krajnjoj tački bez tokena, middleware će blokirati tok i vidjet ćete jasan odgovor o grešci. Čim u zaglavlje uključite važeći token, ista krajnja tačka će vratiti zaštićene podatke , što pokazuje da kontrola pristupa ispravno funkcionira.

Isti obrazac se može ekstrapolirati na više nivoa sigurnosti: možete imati opšti middleware koji provjerava da li je korisnik autentifikovan, i na osnovu toga, druge middleware-ove koji provjeravaju specifične uloge, opsege ili dozvole prije nego što dozvole osjetljivije operacije.

Kontrola pristupa, uloge i najbolje prakse za generiranje tokena

Nakon što ste uspostavili osnovni tok autentifikacije, sljedeći logičan korak je upravljanje ulogama i dozvolama unutar vašeg JWT-a . Da biste to učinili, u korisni teret možete uključiti zahtjeve kao što su uloge (administrator, korisnik, itd.) ili opsege koji određuju koje radnje korisnik može izvršiti.

Vrlo je primamljivo nagurati što više informacija u korisni teret, ali je najbolje odoljeti: JWT nije dizajniran za prijenos velikih količina podataka ili osjetljivih informacija . Kad god je to moguće, ograničite sadržaj na minimum potreban za identifikaciju korisnika i njegovog konteksta autorizacije.

Istek je još jedan kritičan aspekt. Što je kraći vijek trajanja pristupnog tokena, to je manji prozor za napad ako ga neko ukrade . Uobičajena praksa je korištenje pristupnih tokena s kratkim vremenom isteka (na primjer, jedan ili dva sata) i upravljanje upornošću sesije pomoću tokena za osvježavanje.

Tokeni za osvježavanje su sekundarni tokeni, obično s dužim vijekom trajanja i pohranjeni su vrlo sigurno , po mogućnosti u httpOnly kolačićima s omogućenom sigurnosnom zastavom. Njihova funkcija je omogućiti klijentu da dobije nove tokene za pristup bez potrebe za ponovljenim traženjem akreditiva od korisnika.

Sa sigurnosnog stanovišta, vaša aplikacija treba imati mehanizme za poništavanje tokena u kritičnim situacijama , kao što su promjena lozinke, eksplicitna odjava ili otkrivanje sumnjive aktivnosti. Budući da JWT ne čuva stanje, ne postoji lista sesija na serveru, tako da morate koristiti strategije poput crnih lista, keširanja ili agresivnog smanjenja vremena isteka.

Nikada ne zaboravite da je JWT tajni ključ jedna od najosjetljivijih resursa sistema . Uvijek se mora čuvati u varijablama okruženja, pristup kojima mora biti ograničen, a njegova rotacija mora biti planirana u slučaju sumnje na kršenje sigurnosti. Ako neko dobije ovu tajnu, može generirati važeće tokene po volji.

Strategije opoziva, osvježavanje tokena i sigurno skladištenje

Jedan od nezgodnih aspekata JWT-a je rano opoziv aktivnih tokena . Budući da server ne održava stanje za svaki token, jednostavno brisanje iz tabele sesije, kao kod drugih tradicionalnih rješenja, nije dovoljno.

Uobičajena strategija uključuje održavanje crnih lista u memoriji ili u brzoj bazi podataka (kao što je Redis) , gdje se pohranjuju identifikatori tokena koji su opozvani prije datuma isteka. Svaki put kada stigne autentificirani zahtjev, pored provjere potpisa, provjeravate i da li se token nalazi na ovoj listi. Ova strategija je usklađena s praksama upravljanja incidentima u oblaku korištenjem Azurea za poslovne scenarije.

Druga opcija uključuje manipulisanje vremenom: možete postaviti vrlo kratke tokene za pristup i pretpostaviti da, iako ih ne možete odmah opozvati, uticaj vremena je značajno smanjen. Istovremeno, upravljate perzistencijom sesije koristeći tokene za osvježavanje, koje možete eksplicitno poništiti u bazi podataka.

Što se tiče pohrane na strani klijenta, najpreporučljivija lokacija za tokene za osvježavanje u web aplikacijama je httpOnly kolačić sa sigurnim atributima i, u mnogim slučajevima, pravilno konfiguriran sameSite kolačić kako bi se smanjili rizici od XSS-a i CSRF-a. Pristupni token može se pohraniti u memoriju (na primjer, u stanju frontend aplikacije) i obnoviti prije isteka.

Ne zaboravite ovo upotpuniti drugim sigurnosnim mjerama: obaveznom upotrebom HTTPS-a, zaštitnim zaglavljima (Content-Security-Policy, X-Frame-Options, itd.), temeljitom validacijom unosa, kontrolama protiv CSRF-a pri korištenju kolačića i revizijom događaja autentifikacije (neuspjeli pokušaji prijave, promjene lozinke, odjave, itd.).

U korporativnim okruženjima ili velikim projektima, često je preporučljivo kombinovati JWT sa naprednijim sistemima za upravljanje identitetima , internim sigurnosnim politikama i redovnim testiranjem sajber sigurnosti, uključujući testiranje penetracije i preglede arhitekture.

Praktičan primjer: API sa Node.js, Express i MongoDB

Ako razmatrate API spreman za produkciju, obično ćete kombinovati Node.js, Express, MongoDB i Mongoose za izgradnju backend sloja. Korisnički model je definisan šemom koja uključuje obavezna polja, a dodat je i middleware za prethodno spremanje koji šifrira lozinku pomoću bcrypt-a prije nego što je sačuva.

U glavnoj API datoteci, nakon povezivanja na MongoDB i konfiguracije Expressa, definirane su rute /register i /login . U /register, backend prima korisničko ime i lozinku, kreira instancu korisnika, izvršava middleware za šifriranje i sprema korisnika u bazu podataka. Ako sve prođe dobro, odgovara statusnim kodom 201, što ukazuje da je korisnik uspješno kreiran.

U putanji /login, server prvo provjerava da li korisnik postoji; zatim validira lozinku upoređujući je sa pohranjenim hashom. Ako je provjera uspješna, server generira token koristeći jwt.sign, s ID-om korisnika kao glavnim zahtjevom (sub ili id), tajnim ključem pročitanim iz process.env.JWT_SECRET i konfiguriranim vremenom isteka.

Odgovor za prijavu obično uključuje osnovne podatke korisnika i JWT token , što klijentu omogućava da počne pristupati zaštićenim krajnjim tačkama. Alati poput Postmana ili Insomnije mogu lako replicirati ovaj tok: prvo, pozivate `/login`, kopirate vraćeni token, a zatim ga šaljete u zaglavlju autorizacije kada šaljete zahtjeve privatnim rutama kao što je `/clients`.

Ako pokušate ručno izmijeniti token, na primjer promjenom znakova u sadržaju kako biste sebi dodijelili dozvole koje nemate, potpis će postati nevažeći i middleware za autentifikaciju će odbiti zahtjev. Ovo pokazuje vrijednost korištenja HMAC potpisa i posjedovanja robusne i dobro zaštićene tajne.

U nekim projektima, pored korisničkih podataka, funkcija znaka se koristi za uvođenje dodatnih parametara kao što su URI baze podataka, slučajni stringovi ili specifična vremena isteka , a sve se konfiguriše pomoću varijabli okruženja i učitava sa dotenv tako da svako okruženje (razvoj, priprava, produkcija) koristi svoje vlastite vrijednosti.

Autentifikacija kod Googlea i drugih nezavisnih pružatelja usluga

Pored klasične prijave korisničkim imenom i lozinkom, mnoge moderne aplikacije omogućavaju prijave putem Google, Facebook ili sličnih računa . Ideja je iskoristiti sistem autentifikacije treće strane, a zatim izdati vlastiti JWT za internu API upotrebu.

Za integraciju s Googleom, prvi korak je otići na konzolu Google API-ja i kreirati projekat . Nakon kreiranja, omogućite odgovarajući API (ranije Google+, sada Google Identity Services) i generirajte OAuth web vjerodajnice.

U tom procesu morate konfigurirati ovlaštene JavaScript izvore (domene s kojih je dozvoljeno pokretanje toka autentifikacije) i ovlaštene URI-je preusmjeravanja , odnosno putanje u vašoj aplikaciji na koje Google preusmjerava nakon što se korisnik autentificira sa svojim računom.

Kada se korisnik prijavi na Google s frontenda, vaša klijentska aplikacija prima autorizacijski kod. Ovaj kod putuje do backenda putem određene rute, na primjer /auth/google, gdje konstruirate POST zahtjev prema Googleovoj krajnjoj tački, zamjenjujući autorizacijski kod za pristupni token.

U tom pozivu Googleu, šalju se parametri kao što su primljeni kod, client_id i client_secret koje je dostavio Google, povezani redirect_uri i grant_type s vrijednošću authorization_code. Ako je operacija uspješna, Google odgovara tokenom za pristup koji vam omogućava da upitate API korisničkog profila.

S tim pristupnim tokenom u ruci, backend šalje GET zahtjev Google User API-ju, prosljeđujući pristupni token u zaglavlju autorizacije koristeći Bearer format. Ako nema grešaka, u odgovoru ćete dobiti informacije o profilu korisnika (ID, ime, e-mail, fotografiju itd.) .

Odatle, logika je slična bilo kojoj registraciji/prijavi: pretražujete svoju bazu podataka da vidite da li postoji korisnik s providerId-om koji vam Google daje; ako već postoji, jednostavno generirate JWT sa svojim podacima i vraćate ga ; ako ne postoji, kreirate novog korisnika s tim providerId-om, e-poštom, imenom, fotografijom, spremate ga i zatim izdajete token.

Za kreiranje JWT tokena u ovom kontekstu, uobičajeno je koristiti specifičnu uslugu, na primjer tokenService, koja enkapsulira logiku izgradnje korisnog tereta prema specifikaciji (sub, iat, exp i druga polja kao što su email, displayName ili pic) i pozivanje jwt.sign s konfiguriranim algoritmom i privatnim ključem.

Na ovaj način, bez obzira da li se korisnik prijavio putem Google-a, korisničkog imena i lozinke ili drugog provajdera, vaš API uvijek radi s istim JWT formatom , što znatno pojednostavljuje autorizaciju i kontrolu pristupa na ostalim krajnjim tačkama.

U konačnici, integracija eksternih provajdera je odličan način za smanjenje trenja za korisnika i delegiranje dijela autentifikacije dobro testiranim sistemima, bez odustajanja od vlastitog JWT-baziranog sigurnosnog sloja na pozadini.

Savladavanje JWT-a u Node.js API-ju uključuje temeljno razumijevanje njegove interne strukture, poznavanje ispravnog potpisivanja i provjere tokena, korištenje middleware-a za autentifikaciju, osiguranje odgovarajućih ruta, kombiniranje s HTTPS-om, upravljanje tajnama, istecima, osvježavanjem tokena, opozivima i, ako je potrebno, integraciju sa društvenim prijavama poput Googlea; uz pravilnu implementaciju svega ovoga, imate solidnu osnovu za izgradnju sigurnih, skalabilnih servisa spremnih za rast zajedno s vašim projektom.

Nulto povjerenje u Azure Active Directory
Vezani članak:
Zero Trust u Azure Active Directory: šta je to, prednosti i kako ga implementirati

Dodaj kao preferirani izvor na Googleu