OAuth 2.0 autentifikacija: saugi vartotojų prisijungimas

Kas yra OAuth 2.0 ir kodėl jis tapo standartu?

Prisiminkite paskutinį kartą, kai registravotės naujoje svetainėje ir pamatėte mygtuką „Prisijungti su Google” arba „Prisijungti su Facebook”. Tikriausiai neabejodami juo pasinaudojote, nes tai daug paprasčiau nei užpildyti dar vieną registracijos formą ir sugalvoti dar vieną slaptažodį. Už šios patogios funkcijos slypi OAuth 2.0 protokolas – technologija, kuri per pastaruosius metus tapo faktiškai standartiniu būdu valdyti autentifikaciją ir autorizaciją internete.

OAuth 2.0 atsirado kaip atsakas į labai konkrečią problemą: kaip leisti vienai programai gauti prieigą prie jūsų duomenų kitoje programoje, neatskleidžiant jūsų slaptažodžio? Ankstesniais laikais, jei norėjote, kad viena paslauga galėtų pasiekti jūsų duomenis kitoje, dažnai tekdavo tiesiog perduoti savo prisijungimo duomenis. Tai buvo ne tik nesaugu, bet ir nepatogus būdas valdyti prieigą.

Šiandien OAuth 2.0 naudoja beveik visos didžiosios technologijų kompanijos – Google, Facebook, Microsoft, GitHub, Twitter ir daugelis kitų. Tai ne tik patogu vartotojams, bet ir kūrėjams leidžia sukurti saugesnes sistemas be poreikio patiems valdyti slaptažodžių saugojimą ir autentifikacijos procesus.

Kaip veikia OAuth 2.0 mechanizmas?

OAuth 2.0 veikimo principas gali atrodyti sudėtingas, bet iš esmės jis remiasi labai paprasta logika. Įsivaizduokite, kad jūs – tai viešbučio svečias, OAuth serveris – tai registratūra, o aplikacija, kurią norite naudoti – tai viešbučio baseinas. Jums nereikia perduoti baseino darbuotojui savo kambario rakto; užtenka gauti specialią apyrankę iš registratūros, kuri patvirtina, kad esate viešbučio svečias.

Techniškai OAuth 2.0 veikia per kelis žingsnius. Pirma, jūsų aplikacija nukreipia vartotoją į autorizacijos serverį (pavyzdžiui, Google). Ten vartotojas prisijungia ir patvirtina, kad sutinka leisti aplikacijai pasiekti tam tikrus duomenis. Tada autorizacijos serveris grąžina specialų kodą į jūsų aplikaciją. Šis kodas keičiamas į prieigos tokeną (access token), kurį aplikacija gali naudoti API užklausoms atlikti vartotojo vardu.

Svarbu suprasti, kad OAuth 2.0 iš tikrųjų yra autorizacijos, o ne autentifikacijos protokolas. Tai reiškia, kad jis pirmiausiai skirtas spręsti klausimą „ką šis vartotojas gali daryti?”, o ne „kas yra šis vartotojas?”. Tačiau praktikoje OAuth dažnai naudojamas ir autentifikacijai, ypač kartu su OpenID Connect standartu, kuris yra pastatytas ant OAuth 2.0 pamatų.

Skirtingi OAuth 2.0 srautai ir kada juos naudoti

OAuth 2.0 specifikacija apibrėžia kelis skirtingus autorizacijos srautus (flows), ir kiekvienas iš jų skirtas skirtingoms situacijoms. Tai viena iš priežasčių, kodėl OAuth yra toks lankstus ir plačiai naudojamas.

Authorization Code Flow yra saugiausias ir rekomenduojamas būdas daugumai web aplikacijų. Šis srautas veikia per du etapus: pirma gaunamas autorizacijos kodas, tada jis keičiamas į prieigos tokeną. Esminis privalumas – prieigos tokenas niekada neperduodamas per naršyklę, todėl jį sunkiau perimti. Jei kuriate įprastą web aplikaciją su backend’u, tai turėtų būti jūsų pasirinkimas.

Implicit Flow anksčiau buvo populiarus single-page aplikacijoms (SPA), nes prieigos tokenas gaunamas iš karto, be papildomo keitimo žingsnio. Tačiau šiuolaikinėse rekomendacijose šis metodas laikomas nesaugiu, nes tokenas perduodamas per URL fragmentą ir gali būti lengviau pažeidžiamas. Jei kuriate SPA, geriau naudokite Authorization Code Flow su PKCE.

Client Credentials Flow skirtas situacijoms, kai aplikacija kreipiasi į API savo vardu, o ne vartotojo vardu. Pavyzdžiui, jei turite backend servisą, kuris kas naktį sinchronizuoja duomenis su kitu servisu. Čia nėra vartotojo sąveikos – tiesiog aplikacija autentifikuojasi naudodama savo kredencialus.

Resource Owner Password Credentials Flow leidžia aplikacijai tiesiogiai priimti vartotojo prisijungimo vardą ir slaptažodį. Tai prieštarauja pagrindinei OAuth idėjai ir turėtų būti naudojama tik labai specifiniais atvejais, pavyzdžiui, kai kuriate oficialią mobilią aplikaciją savo paties servisui.

PKCE: papildoma apsauga mobilioms ir SPA aplikacijoms

Proof Key for Code Exchange (PKCE, tariama „piksė”) yra OAuth 2.0 pratęsimas, kuris tapo būtinas kuriant šiuolaikines aplikacijas. Jis buvo sukurtas spręsti specifinę saugumo problemą mobiliose aplikacijose, bet dabar rekomenduojamas beveik visur.

Problema, kurią PKCE sprendžia, yra ta, kad mobiliose aplikacijose ir SPA neįmanoma saugiai laikyti client secret. Jei įdėtumėte slaptą raktą į mobilią aplikaciją, bet kas galėtų ją išardyti ir jį rasti. PKCE sprendžia šią problemą naudodamas dinamiškai generuojamus kodus vietoj statinio slapto rakto.

Veikimo principas gana elegantiškas. Aplikacija sugeneruoja atsitiktinę eilutę, vadinamą code verifier. Tada sukuria šios eilutės hash’ą (code challenge) ir siunčia jį kartu su autorizacijos užklausa. Kai gaunamas autorizacijos kodas ir keičiamas į tokeną, aplikacija turi pateikti originalų code verifier. Serveris patikrina, ar jo hash’as atitinka anksčiau gautą code challenge.

Tokiu būdu, net jei kažkas perims autorizacijos kodą, jis negalės jo panaudoti be originalaus code verifier, kuris niekada nebuvo perduotas per tinklą nesuhash’uotas. Šis mechanizmas veikia net jei aplikacija neturi client secret.

Tokenų valdymas ir atnaujinimas

Vienas iš dažniausiai klaidingai suprantamų OAuth aspektų yra tai, kaip veikia prieigos tokenai ir refresh tokenai. Supratimas šių skirtumų yra kritiškai svarbus kuriant saugią sistemą.

Prieigos tokenas (access token) yra tai, ką jūsų aplikacija naudoja kiekviename API užklausime. Jis paprastai yra trumpalaikis – galioja nuo kelių minučių iki kelių valandų. Tai svarbi saugumo priemonė: jei tokenas būtų pavogtas, jis greitai pasibaigs ir taps nenaudingu. Prieigos tokenai dažniausiai būna JWT formato, nors tai nėra privaloma.

Refresh tokenas yra ilgalaikis tokenas, kuris naudojamas gauti naujus prieigos tokenus, kai senieji baigiasi. Jis laikomas daug saugiau nei prieigos tokenas ir niekada neturėtų būti siunčiamas API užklausose. Refresh tokenai turėtų būti saugomi saugioje vietoje – serveryje arba, jei būtina kliento pusėje, tai tik su papildomomis apsaugos priemonėmis.

Praktiškai tai veikia taip: vartotojas prisijungia ir gauna abu tokenus. Aplikacija naudoja prieigos tokeną API užklausoms. Kai API grąžina klaidą, kad tokenas nebegalioja, aplikacija automatiškai naudoja refresh tokeną gauti naują prieigos tokeną. Vartotojas net nepastebi šio proceso ir neturi prisijungti iš naujo.

Svarbu implementuoti refresh token rotation – kiekvieną kartą naudojant refresh tokeną, turėtų būti išduodamas naujas refresh tokenas, o senasis tampa negaliojančiu. Tai sumažina riziką, jei refresh tokenas būtų pavogtas.

Dažniausios saugumo klaidos ir kaip jų išvengti

Net naudojant OAuth 2.0, vis dar galima padaryti klaidų, kurios sukuria saugumo spragas. Štai keletas dažniausiai pasitaikančių problemų, su kuriomis susidūriau peržiūrėdamas įvairių projektų kodą.

Pirmiausia – netinkamas redirect URI validavimas. Kai autorizacijos serveris grąžina vartotoją atgal į jūsų aplikaciją, jis naudoja redirect URI. Jei šis URI nėra griežtai validuojamas, piktavalis gali nukreipti vartotoją į savo kontroliuojamą svetainę ir pavogti autorizacijos kodą. Visada registruokite tikslų redirect URI, ne tik domeną, ir niekada nenaudokite wildcard simbolių.

Antra problema – state parametro ignoravimas. State parametras yra atsitiktinė reikšmė, kurią jūsų aplikacija sugeneruoja prieš pradedant OAuth srautą ir patikrina, kai vartotojas grįžta. Tai apsaugo nuo CSRF atakų. Daug kūrėjų praleidžia šį parametrą, nes jis nėra privalomas, bet tai yra rimta saugumo spraga.

Trečia – tokenų saugojimas nesaugiose vietose. Prieigos tokenai niekada neturėtų būti saugomi localStorage ar sessionStorage, nes jie prieinami JavaScript kodui ir gali būti pavogti per XSS atakas. Jei įmanoma, naudokite HttpOnly cookies. Jei kuriate SPA, apsvarstykite Backend-for-Frontend (BFF) pattern, kur visas OAuth srautas vyksta serveryje.

Ketvirta – nepatikrintas token scope. Kai gaunate prieigos tokeną, patikrinkite, ar jis turi reikiamus scope (leidimus). Nepasitikėkite vien tuo, kad prašėte tam tikrų scope – vartotojas galėjo jų nesuteikti, arba kažkas galėjo pakeisti tokeną.

Praktinis implementavimas su populiariausiomis bibliotekomis

Nors OAuth 2.0 specifikacija yra gerai dokumentuota, implementuoti ją nuo nulio būtų neprotinga. Laimei, beveik kiekvienai programavimo kalbai ir framework’ui yra patikimų bibliotekų.

JavaScript/Node.js ekosistemoje populiariausi pasirinkimai yra Passport.js su įvairiais OAuth strategijomis, arba naujesnis ir modernesniesnis next-auth (dabar Auth.js). Jei kuriate React aplikaciją, Auth.js puikiai integruojasi su Next.js ir palaiko daugybę OAuth provider’ių iš dėžės. Konfigūracija paprastai atrodo maždaug taip:


providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID,
clientSecret: process.env.GOOGLE_CLIENT_SECRET,
}),
]

Python pasaulyje Authlib yra viena geriausių bibliotekų tiek OAuth klientams, tiek serveriams. Ji palaiko visus OAuth 2.0 srautus ir yra labai gerai dokumentuota. Django projektams django-allauth yra puikus pasirinkimas, nes integruoja ne tik OAuth, bet ir tradicinę autentifikaciją.

.NET kūrėjams Microsoft.AspNetCore.Authentication.OAuth paketas yra natūralus pasirinkimas, ypač jei naudojate ASP.NET Core. Jis puikiai integruojamas su Identity sistema ir palaiko daugybę provider’ių.

PHP bendruomenėje League OAuth2 Client yra de facto standartas. Jis turi aiškią API ir daugybę oficialių bei bendruomenės palaikomų provider’ių paketų.

Svarbu pasirinkti aktyviai palaikomą biblioteką su gera dokumentacija. Patikrinkite, kada buvo paskutinis atnaujinimas, ar yra atvirų saugumo problemų, ir ar biblioteka palaiko šiuolaikinius standartus kaip PKCE.

OAuth kaip paslauga: kada verta naudoti trečiųjų šalių sprendimus

Kartais pačiam implementuoti OAuth infrastruktūrą nėra prasmės. Jei kuriate aplikaciją, kuri pati turi būti OAuth provider’is (t.y. kiti nori integruotis su jumis), arba jei norite centralizuoto autentifikacijos valdymo kelioms aplikacijoms, verta apsvarstyti specializuotus sprendimus.

Auth0 yra vienas populiariausių „authentication as a service” sprendimų. Jie tvarko visą OAuth srautą, vartotojų valdymą, multi-factor authentication ir daug kitų funkcijų. Jūsų aplikacija tiesiog integruojasi su jų API. Tai ypač patogu, jei kuriate kelis produktus ir norite vieningos autentifikacijos patirties. Minusas – tai papildomi kaštai ir priklausomybė nuo trečiosios šalies.

Keycloak yra open-source alternatyva, kurią galite patys host’inti. Tai pilnavertis identity ir access management sprendimas, palaikantis OAuth 2.0, OpenID Connect, SAML ir kitus standartus. Jei turite resursų jį administruoti, tai puikus pasirinkimas, ypač enterprise aplinkose.

Ory yra kitas įdomus open-source projektas, kuris siūlo modulinį požiūrį – galite naudoti tik tuos komponentus, kurių reikia. Jų OAuth2 serveris (Ory Hydra) yra labai greitas ir skalus.

Sprendžiant, ar naudoti trečiųjų šalių sprendimą, apsvarstykite kelis faktorius: komandos dydį ir kompetenciją, saugumo reikalavimus, biudžetą, ir ar jums reikia tik OAuth kliento funkcionalumo, ar pilno provider’io sprendimo. Jei tiesiog norite leisti vartotojams prisijungti per Google ar Facebook, pakanka paprastos bibliotekos. Jei kuriate platformą, kuri pati turi būti OAuth provider’is, specializuotas sprendimas gali sutaupyti mėnesius darbo.

Ateities perspektyvos ir OAuth evoliucija

OAuth 2.0 nėra statiškas standartas – jis nuolat evoliucionuoja per įvairius pratęsimus ir patobulinus. OAuth 2.1, kuris šiuo metu yra draft stadijoje, konsoliduoja geriausias praktikas ir pašalina pasenusias funkcijas.

Vienas svarbiausių OAuth 2.1 pakeitimų – PKCE tampa privalomas visiems klientams, ne tik public klientams. Tai atspindi realybę, kad net confidential klientai gali turėti saugumo problemų. Implicit flow bus visiškai pašalintas iš specifikacijos, nes jis laikomas nesaugiu.

Kitas įdomus vystymasis yra Demonstrating Proof-of-Possession (DPoP) – mechanizmas, kuris užtikrina, kad prieigos tokenas gali būti naudojamas tik to kliento, kuriam buvo išduotas. Tai apsaugo nuo token theft atakų, net jei piktavalis kaip nors gautų prieigos tokeną.

Pushed Authorization Requests (PAR) yra kitas naujas standartas, kuris pagerina saugumą perkeliant autorizacijos parametrus iš URL į tiesioginį POST užklausą į autorizacijos serverį. Tai sumažina riziką, kad parametrai būtų perimti ar modifikuoti.

Taip pat matome didėjantį dėmesį decentralizuotai identifikacijai ir self-sovereign identity koncepcijoms. Nors OAuth lieka centralizuotas modelis, ateityje galime matyti hibridinių sprendimų, kurie derina OAuth patogumą su decentralizuoto modelio privalumais.

Praktiškai tai reiškia, kad jei šiandien implementuojate OAuth, turėtumėte laikytis šiuolaikinių geriausių praktikų – naudoti PKCE, vengti implicit flow, rūpintis token saugumo. Tokiu būdu jūsų implementacija bus suderinama su būsimais standartais ir nereikės didelių pakeitimų.

Svarbu sekti OAuth darbo grupės (IETF OAuth WG) veiklą ir atnaujinti savo bibliotekas. Saugumo kraštovaizdis nuolat keičiasi, ir tai, kas buvo laikoma saugiu prieš penkerius metus, šiandien gali būti pažeidžiama. Geros naujienos yra tai, kad jei naudojate patikimas, aktyviai palaikomas bibliotekas, dauguma šių atnaujinimų vyks automatiškai, kai atnaujinsite priklausomybes.

OAuth 2.0 tapo neatsiejama šiuolaikinės web architektūros dalimi, ir šis statusas greičiausiai išliks dar ilgus metus. Supratimas, kaip jis veikia, kokie yra jo stiprybės ir silpnybės, ir kaip jį teisingai implementuoti, yra būtina kompetencija kiekvienam šiuolaikiniam kūrėjui. Nesvarbu, ar kuriate mažą startup projektą, ar enterprise sistemą – OAuth 2.0 suteikia reikiamą lankstumą ir saugumą, jei tik naudojate jį teisingai.

Daugiau

Python context managers: with sakiniai