Keresés tartalomra
Kategóriák
Címkék
- Java
- Spring
- Python
- IoC
- Android
- DI
- Dagger
- Thymeleaf
- Markdown
- JDK11
- AOP
- Aspect
- Captcha
- I18n
- JavaSpark
- Microframework
- Testing
- JUnit
- Security
- JWT
- REST
- Database
- JPA
- Gépház
- WebFlux
- ReactiveProgramming
- Microservices
- Continuous Integration
- CircleCI
- Deployment Pipeline
- Docker
- Mocking
- LogProcessing
- PlantUML
- UML
- Modellezés
- OAuth2
- Node.js
- DevOps
- Websocket
OAuth 2 authorizáció - Ismerkedés a framework lehetőségeivel
Az első és legfontosabb kérdés nyilván az, hogy mi is az OAuth 2? Talán mondanom sem kell, mennyire fontos - különösen a publikus internetre kilógatott - alkalmazásaink védelme az illetéktelen hozzáféréstől. Erre számos módszer létezik, az egészen ősi Basic autentikációtól kezdve, a bejelentkező űrlap alapún át a különböző token alapú megoldásokig, de még bőven lehetne sorolni az opciókat. Közös problémájuk, hogy ezek önmagukban csak a felhasználó hitelesítéséért (autentikáció) felelnek, a felhasználói jogosultságok (autorizáció) kiosztása és ellenőrzése más komponensek feladata.
Ezen a ponton rögtön érdemes is tisztázni tehát a két folyamat közti különbséget. A felhasználó hitelesítése, azaz bejelentkeztetése, effektíve annak megállapítása, hogy a felhasználó valóban létezik az adott rendszerben, létezik egy aktív fiókja, és úgy általában jogosult az adott rendszer használatára, az autentikáció folyamata. Ez a legtöbb esetben úgy történik, hogy a felhasználót egy bejelentkező oldalra navigáljuk amikor az egy védett erőforrást próbál elérni, ahol a felhasználó megadja a felhasználónevét és jelszavát, opcionálisan elvégzi a rendszer a kétfaktoros verifikálást és kész vagyunk. Ezen a ponton még csak azt tudjuk, hogy a felhasználó jogosult a rendszer használatára, de hogy azon belül mit tehet meg, arról még nincs információnk. Itt lép képbe az autorizációs lépés, mely pontosan arra a kérdésre fog választ adni, hogy a belépett felhasználó mit és hogyan tehet meg az adott rendszerben. Például a Leaflet esetében az "alap" felhasználók megtekinthetik a blogbejegyzéseket, de nem írhatnak újakat vagy szerkeszthetnek meglévőket - azokra csupán a szerkesztő, vagy afölötti szintű felhasználók jogosultak. A példából már érződhet is, hogyan történik az autorizáció a klasszikus formájában: a felhasználók szerepkörökhöz vannak rendelve, ezek ellenőrzésével tudja a rendszer meghatározni, hogy az adott felhasználó mire jogosult és mire nem. Sok esetben azonban a szerep (role) alapú megkülönböztetés nem elég szemcsézett - adott esetekben két azonos szintű felhasználó között is indokolt lehet különbséget figyelembe venni, például ha egy felhasználó ír egy kommentet, az szerkeszteni is tudja azt (tehát role szinten van joga szerkesztésre és létrehozásra), de csak a sajátját. A szemcsézéssel egy lépéssel tovább kell mennünk és ACL-t (Access Control List - hozzáférés-irányítási lista) kell bevezetnünk.
És akkor ezzel elérkeztünk oda, hogy hogyan segít nekünk ebben az OAuth 2.0 framework, vagy ahogy a hivatalos dokumentációjában jellemzik, az "OAuth 2.0 ipari-standard autorizációs protokoll". Az OAuth 2.0 (továbbiakban OAuth) az autorizáció kezelésére ad irányvonalakat egy standardizált keretrendszer formájában. Az autorizáció alapját hozzáférési tokenekben határozza meg, a teljes keretrendszer pedig ennek a tokennek a létrehozásában segít. Mivel ipari standard, a framework előírásait követő rendszerek között könnyű az átjárás, gyakorlatilag a consumer és producer (a pontos terminológiára hamarosan kitérünk) service-ek gyors átkonfigurálásával könnyen kicserélhető az autorizációt biztosító rendszer. Fontos megjegyezni, hogy a framework maga az autentikációs folyamatokra nem ad standardot, az az autorizáló rendszerre van bízva, hogyan lépteti be a felhasználót. A jogosultságok meghatározása és a hozzáférési token biztonságos kiállítása, valamint annak későbbi (rendszeres) verifikálása lesz a OAuth frameworköt implementáló rendszer feladata. Ugyanakkor azt is fontos megemlíteni, hogy az OAuth frameworkhöz számtalan kiegészítés is létezik RFC formában, melyek tovább bővítik a framework ajánlásait, így a legtöbb esetben maga az autorizációt végző rendszer oldja meg az autentikációt is, a megfelelő extra ajánlásokat követve.
OAuth komponensek
Ahhoz, hogy a továbbiakban érthető legyen, milyen komponensek együttműködése szükséges egy OAuth-alapú rendszer működéséhez, először is érdemes tisztázni az OAuth folyamatokban résztvevő szereplőket, komponenseket.
-
Resource Owner
Az az entitás, aki egy adott erőforrás elérésére jogosult - tipikusan egy felhasználó, de OAuth környezetben ez egy gépi entitás is lehet. Tükörfordításban az "erőforrás tulajdonosa" az, aki lehetővé teszi egy alkalmazásnak (a kliensnek, a következő komponensünk), hogy az elérje az általa birtokolt erőforrásokat - egyúttal azt is meghatározva, hogy hogyan teheti ezt meg (például csak olvashatja, vagy módosíthatja is). -
A kliens alkalmazás
A második komponensünk az a kliens alkalmazás, ami a Resource Owner nevében hozzáfér az adott erőforrásokhoz. Természetesen ezt csak azután teheti meg, hogy a felhasználó erre engedélyt adott neki. A kliens alkalmazásokat minden esetben regisztrálni kell az OAuth rendszerben, mivel minden kliens alkalmazás köteles azonosítani magát az autorizáció során - később erre még visszatérünk, hogyan. -
Resource Server
Az az alkalmazás, amely rendelkezik azokkal az erőforrásokkal, melyekhez a kliens alkalmazás a Resource Owner nevében szeretne hozzáférni. Ha nagyon le szeretnénk egyszerűsíteni, ez egy service, pl egy REST API, amit OAuth-tal védünk. Hogy egy konkrét példával is éljek, a felhasználói fiókokat a Leaflet kezeli (ő tehát egy Resource Server), a felhasználói fiók viszont egy adott felhasználóhoz tartozik (ő a Resource Owner). Az az alkalmazás azonban, melyen keresztül ez a cikk is olvasható, lehetőséget ad a felhasználónak az adatai módosítására, tehát ez egy kliens. Bejelentkezés során a kliens hozzáférést kér a felhasználótól annak adataihoz, mellyel aztán a Leafletet meghívva el tudja végezni a kért módosításokat. -
Authorization Server
Végül az egész folyamat koordinálásáért az Authorization Server felel. Ő kezeli a regisztrált klienseket, fogadja az autorizációs kéréseket, állítja ki a hozzáférési tokeneket, illetve indokolt esetben végzi azok utólagos, akár folyamatos verifikálását. Bár az OAuth framework nem ad rá konkrét útmutatást, hogyan végezzük vele a felhasználó autentikációját, de a legtöbb esetben maga az Authorization Server feladata ez is.
A hozzáférési tokenek
Ahogy azt már említettem, az OAuth frameworkben a hozzáférés alapját a hozzáférési (access) tokenek képzik. Ezek a tokenek jelzik a kliens alkalmazás és a Resource Server számára is, hogy az adott felhasználó, illetve az alkalmazás a felhasználó nevében mit és hogyan érhet el. De hogyan is néznek ki ezek a tokenek? Bár az OAuth framework nem határozza meg szigorúan a token formátumát, két megközelítést javasol, melyekre egész konkrét specifikációt is meghatároz.
-
Opaque tokenek
Az opaque tokenek "kriptográfiailag véletlenszerű" karakterláncok, melyek semmilyen konkrét információt nem tartalmaznak a felhasználó kilétéről vagy annak jogosultságairól (emiatt opaque tokenekkel működő Authorization Serverek úgynevezett ID tokeneket is kiállítanak, melyek a kliens számára tartalmaznak általános információkat a felhasználóról, úgy mint a neve, email címe, általános szerepköre, beállított nyelve, stb.). Emiatt azonban a Resource Server-ek sem tudnak mit kezdeni az opaque tokenekkel önmagukban, így az Authorization Server lehetőséget kell biztosítson a token által azonosított felhasználó kilétének felfedésére. Ebben az esetben az Authorization Server köteles minden kiállított tokent "követni", effektíve egy referenciát kell tároljon memóriában, vagy egy (lehetőleg nagyon gyors elérésű) adatbázisban, amely segítségével meg tudja mondani a tokenhez rendelt jogosultságokat. A Resource Server-ek ezen követés segítésével oldják fel a jogosultságokat. -
Önleíró JWT tokenek
A másik javasolt opció a JWT tokenek használata. Ezek "önleíró" tokenek, a token Base64 kódolt formátumban tartalmaz minden fontos információt a felhasználóról (beleértve a nevét, email címét, jogosultságait, stb.). Mivel ezek ID tokenként is tudnak működni, nem szükséges külön ID token kiállítása. Nagyon fontos, hogy ezek a tokenek könnyen manipulálhatóak "aláírás" nélkül, és bár a JWT specifikáció szerint nem kötelező a token aláírása, a legtöbb esetben (akár akkor is ha nem OAuth környezetben használjuk) az aláírás lényegében elengedhetetlen. Az aláírás biztosítja, hogy a token tartalma nem lett módosítva a kiállítása után, mivel bármilyen módosítás a token payload-jában (a felhasználói adatokat tartalmazó részében) az aláírás érvénytelenné válását okozza. Önleíró működése miatt a JWT tokenekhez nem szükséges token követés az Authorization Server oldalán (de extra biztonsági lépésként ez sem megoldhatatlan).
Az, hogy melyik megoldást választjuk, teljesen rajtunk áll. A Spring Framework OAuth támogatása például mindkét opció használatát lehetővé teszi, a framework automatikusan végzi a megfelelő ellenőrzéseket. Nagyon fontos megjegyezni, hogy bármelyik módszert is válasszuk, a hozzáférési tokent rövid lejárati idővel érdemes kiállítani, így biztosítva azt, hogy ha a tokent el is lopják, azt ne lehessen sokáig használni.
Authorization Grant Flow-k
Az autorizációs folyamatot az OAuth több, különböző felhasználási célú úgynevezett Authorization Grant Flow-kban határozza meg. Néhány fontosabb ezek közül a következő:
Authorization Code Grant Flow
Rögtön a talán legfontosabbal és legelterjedtebbel kezdenék, ráadásul ez pont egy olyan flow, amivel már mindenki találkozott, aki csak egyszer is a "Bejelentkezés Google/Facebook/GitHub/... fiókkal" gombra kattintott bármilyen, nem az ezekhez a szolgáltatókhoz tartozó oldalon. Ilyen esetben mindig nagyon hasonló a folyamat: ha nem vagyunk az adott fiókkal bejelentkezve, akkor be kell jelentkeznünk, ellenkező esetben rögtön kapunk egy gombot, amivel engedélyezhetjük a hozzáférést az alkalmazás számára, illetve minden esetben láthatjuk azt is, hogy milyen jogokat adunk az adott alkalmazásnak. Ez az OAuth Authorization Code Flow autorizációs folyamata, annak is az első, a felhasználó számára is látható része. A folyamat eredménye egy úgynevezett "authorization code", melyet aztán a háttérben az alkalmazás egy hozzáférési tokenre cserél. A 1st party rendszer ezután ezzel a tokennel végez minden további kérést a saját Resource Server-ei felé.
Authorization Code Grant Flow + PKCE
Az Authorization Code Flow egy specifikusan JavaScript alapú kliens alkalmazások számára átalakított változata. A következő fejezetben erről kicsit részletesebben szó lesz, de az autorizációs folyamatban a kliens köteles azonosítania magát, mely egy azonosítópárral történik. JS alkalmazások esetén azonban ez azt jelentené, hogy az azonosítópár "jelszó" része megjelenne az alkalmazás publikusan is olvasható kódjában. Ennek kiküszöbölésére egy speciális "kézfogást" hajt végre a kliens és az Authorization Server, ami a PKCE. Ebben a cikkben nem térnék ki a részleteire, de egy véletlenszerűen generált karaktersorozatot használ a kliens, azt elhasheli és elküldi az Authorization Server-nek. A token kérés (2. autorizációs lépés) során, újra elküldi az eredeti karaktersorozatot illetve az általa használt hashelési algoritmust. Az Authorization Server ezt összeveti az első lépésben kapott hash-sel és ha egyezik, akkor a kliens "bejelentkezése" sikeres.
Refresh Token Grant Flow
Az Authorization Code Grant Flow kiegészítése, mely arra szolgál, hogy a hozzáférési token lejáratakor a rendszer automatikusan tudjon igényelni egy újat. Ehhez az szükséges, hogy az Authorization Server a hozzáférési token kiállításakor egy refresh tokent is kiállítson. Fontos megjegyezni, hogy a refresh token tipikusan hosszú (akár végtelen) lejárati idejű, és ellopása esetén a "tolvaj" végtelen számú új hozzáférési tokent igényelhet. Így fontos megoldani a refresh token visszavonásának lehetőségét.
Client Credentials Grant Flow
Egy-lépéses autorizációs folyamat, mely csak a token-igénylésből áll és kizárólag service-to-service kommunikációra ajánlott. Külön felhasználói azonosítás nem történik, két alkalmazás egymás közti kommunikációját tudjuk védeni az így kiállított hozzáférési tokenekkel.
Device Grant Flow
Az IoT eszközök autorizációját elősegítő kiterjesztése az OAuth frameworknek. Működését olyan esetekben érhetjük tetten, mint például okostévék beléptetése streaming platformokra.
Vannak még további Grant Flow-k, melyek már a framework-ben deprekált, "legacy" módszerekként vannak hivatkozva. Ezek nem feltétlenül jelentik azt, hogy nem használhatóak már, azt sem feltétlenül jelentik, hogy nem biztonságosak, de ha új rendszert építünk, ezeket a Grant Flow-kat már érdemes kerülni, és valamelyik "modernebb", jobban támogatott Grant Flow-ra alapozni az autorizációt. Részleteiben most nem mennék bele a fenti Grant Flow-k működésébe, az egy következő cikk tartalma lesz!
Kliens alkalmazás regisztráció és konfiguráció
OAuth frameworkben minden alkalmazás egy ugyanolyan autentikálandó entitás, mint akár egy felhasználó. Ennek megfelelően, a framework előírásai elvárják, hogy a résztvevő felek regisztrálva legyenek a rendszerben - tehát nem csak a kliens alkalmazások, de a Resource Server-ek is. Általánosságban a következő paraméterekkel fognak rendelkezni a regisztrált alkalmazások:
- Client ID és Client Secret: ez egy azonosítópár, amivel minden regisztrált OAuth alkalmazásnak rendelkeznie kell - nagyjából ugyanaz a szerepük, mint egy felhasználónév és jelszó párnak. Bármely autorizációs folyamatot nézzük, ezt az azonosítópárt mindenképpen használnia kell a kliens alkalmazásnak, mikor hozzáférési tokent próbál kérni (kivéve az Authorization Code Grant + PKCE Flow esetében).
- Az engedélyezett Authorization Grant Flow az adott klienshez (Resource Server-ek esetén): Meghatározza, hogy milyen Grant Flow-kat kérhetünk az adott Resource Server-től.
- Engedélyezett visszairányítási cím (Authorization Code Flow-val regisztrált kliens alkalmazás esetén): Meghatározza, hogy mi az a cím, ahova az Authorization Server visszairányíthatja a böngészőt az autorizációs folyamat végén.
- Engedélyezett kliens kapcsolatok (Resource Server-ek esetén): Meghatározza, mely regisztrált kliens alkalmazások hívhatják meg az adott Resource Servert és azok milyen jogosultságokat, úgynevezett scope-okat kérhetnek.
- Az adott Resource Server "audience" értéke: ez egy extra azonosító, mellyel a Resource Server-ek rendelkezhetnek. A kiállított hozzáférési tokenben meghatározhatjuk, hogy az adott token milyen "audience" számára érvényes és használható, így egészen leszűkítve az adott token felhasználhatóságát.
Egy-egy új alkalmazás regisztrációja során a fenti paraméterek legjavát meg kell határoznunk, de hogy mik a kötelező értékek és milyen lehetőségeink vannak, az mindig az implementáló rendszeren múlik. Számos 3rd-party Authorization Provider szolgáltatás létezik, amelyek specifikusan OAuth autorizálást biztosítanak, mint például az auth0 vagy az Okta, de számos egyéb szolgáltatás is biztosít amolyan mellékszolgáltatásként OAuth bejelentkezést és autorizálást, úgy mint a Google, GitHub, Facebook, Microsoft stb. Illetve persze készíthetünk saját Authorization Server-t is, ekkor mi határozzuk meg, mire lesz szükségünk. A Spring Frameworkhöz korábban volt out-of-the-box Authorization Server megoldás, mely jelenleg már teljesen deprekált állapotban van, az új támogatás felépítése pedig még mindig folyamatban van.
Persze, az is egy nagyon jó kérdés, hogy hogyan regisztráljuk az alkalmazást. Az általam még építés alatt álló megoldás egyelőre "statikus" konfigurációból fog dolgozni, azonban az OAuth framework egyik kiegészítése éppen a dinamikus alkalmazás regisztrációról szól, így kiegészítésben, de erre is ad javasolt megoldást a framework. A fentebb felsorolt szolgáltatók pedig minden esetben dinamikus alkalmazásregisztrációval dolgoznak.
A továbbiakban...
Ez a cikk csak egy rövid bevezetés akart lenni az OAuth Framework világába, így most rövidre is zárnám. A továbbiakban (a következő 1 vagy 2 cikkben) mélyebbre ásunk az OAuth működésében, megnézzük milyen endpoint-okkal dolgozik egy Authorization Server API, hogyan működnek az egyes Grant Flow-k, mit tartalmaznak a JWT tokenek és azokat hogyan verifikálják a Resource Server-ek, és még akár a jelenleg is építés alatt álló saját Authorization Server-emet is bemutatom. Remélhetőleg ez utóbbit hamarosan élesben is láthatjátok majd - ha befejeződik az átalakítás, arról egy rövid helyzetjelentést mindenképp írok majd, de amúgy is látni fogjátok, hogy némiképp megváltozik majd a bejelentkezési folyamat.
Referenciák
OAuth 2.0 egyszerűsített útmutató és sandbox tesztkörnyezet
Komment írásához jelentkezz be
Bejelentkezés
Még senki nem szólt hozzá ehhez a bejegyzéshez.