Representational State Transfer - saját REST service-nél felhasználó azonosítás OpenID-vel? Egyáltalán lehetséges ez?

Sok weboldal használ OpenID-t ahhoz, hogy regisztrálja és beléptesse a felhasználóit. Egészen elszállt esetekben akár egy tucat identity provider is fel van sorolva, mint lehetőség, pl Google, Microsoft, Yahoo, PayPal, Facebook, Twitter, stb... A Facebook és a Twitter tudtommal nem OpenID-t használnak, de nekik is van REST API-juk, amin keresztül identitást el lehet kérni. Van még kismillió ilyen cég. Lényeg a lényeg, rajtuk keresztül tudjuk elkérni valakinek az identitását OAuth2-t használva. Ezt most nem részletezem, hogy hogyan zajlik, elég annyi, hogy a felhasználó összekattintja a böngészőben, hogy engedi nekünk, hogy hozzáférjünk az email címéhez, és pár HTTP redirect után a 3rd party REST kliensünk megkapja az email címet. Ezek után a legtöbb oldal beteszi szerver oldali session-be ezt az info-t, esetleg a hozzá tartozó user id-t, és pont.



Ami szerintem egy igen komoly veszélyforrás, hogy kérdés nélkül megbízunk ezekben az identity provider-ekben. Nem tudom, hogy pontosan mi áll a szerződésekben, amikor 3rd party klienst regisztrálunk egy-egy ilyen cégnél, de a gyakorlatban bármikor gondolhatnak egyet, és visszaélhetnek valaki identitásával. Ugyanezt egy ügyes hacker is megteheti ha sikerül identity provider-nek álcáznia magát. Ha nem ezeknek a cégeknek a szolgáltatásaira alapozzuk a weboldalunkat vagy saját webservice-ünket, akkor érdemes jól átgondolni, hogy megéri e a kockázatot, esetleg beépíteni egy olyan failsafe mechanizmust a rendszerbe, ami kizárthat identity provider-eket, ha esetleg borul a bili.

A REST service-eket általában véve gépi klienseknek találták ki. Az azonosítás általában véve úgy megy ezeknél, hogy minden kérésnek van egy azonosító és egy secret része. Így az azonosítóból tudjuk, hogy ki akar végrehajtani valamit, a secret-ből meg tudjuk, hogy erre jogosult e. Nagyjából ugyanez történik a session cookie esetében is, azzal a különbséggel, hogy ott csak egy secret megy el, a session id, és abból a szerver oldali tároló alapján találja ki az alkalmazás, hogy kicsoda, és hogy jogosult e az adott műveletre. A különbség nem véletlen, a stateless constraint-nek köszönhető.

A REST service-ek úgy kerülnek a képbe, hogy single page javascript application (SPA) esetében szerver oldalra be lehet tenni egy REST service-t. Így a SPA, mint REST kliens ettől kéri el az adatokat. A megfelelő beállításokkal a böngészőből más kliens nem fér hozzá a szolgáltatáshoz, a credentials meg elmehet az Authorization header-ben. Ennek hátulütője hogy egy sima javascript injection-el bármihez hozzáférhetnek, szóval biztonsági szempontból nagyon alaposnak kell lenni, amikor az adatokat ellenőrizzük, kezeljük.

Nekem az a perverz gondolatom támadt, hogy mi lenne ha a már említett identity provider-eket használnánk egy ilyen SPA-s alkalmazásnál a felhasználók azonosítására? Az eddigi aggályokat félretéve érdemes megvizsgálni a kérdést, mert korántsem annyira egyértelmű, hogy ez hogyan valósítható meg. Mire van szükség?
  • Az SPA REST kliensnek minden kéréssel el kell küldeni a saját REST service felé az identitást (email cím) egy secret társaságában. Így meg lehet mondani, hogy kinek mondja magát, és hogy tényleg az e, illetve stateless marad a kommunikáció.
  • Az SPA REST klienshez valahogy el kell juttatni az email címet és egy secret-et, amit a szerver oldalon kell generálni rejtve a kíváncsi szemek elől.
  • A secret generálás előtt el kell kérni az email címet a szerver oldalon egy 3rd party REST kliens segítségével, ami az adott identity provider-nél be van regisztrálva.
  • Minden kommunikációnak HTTPS-en keresztül kell mennie.
Hogyan köthetőek össze az elemek? Szvsz. itt szükség van egy saját identity provider-re, amihez az SPA kliens hozzá tud kapcsolódni, és elkérni tőle a szükséges infot. Ez lehet a REST service domain-jén vagy az SPA kliens domain-jén is, nincs megkötve a dolog. A lényeg csak az, hogy szerver oldalon fusson, tehát titokban.

Az azonosítás nagyjából így megy:
  1. A kliens látja, hogy az illető nincs bejelentkezve, tehát megjelenik a Google beléptetős gomb.
  2. A felhasználó kattint, a gomb átirányít a saját identity provider-hez azzal, hogy Google beléptetés szükséges.
  3. A saját identity provider a 3rd party kliensnek hozzáférést kér a Google-től, átirányít a Google szerverére. Ott a felhasználó belép, ha szükséges, és megadja a hozzáférést a 3rd party kliensnek, ha szükséges.
  4. A Google visszairányít a saját identity provider-hez, és hozzáférést biztosít az email címhez, amit el is kér a saját identity provider a Google-től. 
  5. A saját identity provider visszairányít az SPA klienshez, és valahogy átadja az email címet és a digitális aláírást.
  6. Az SPA kliens ezentúl minden kéréssel elküldheti az email címet és a digitális aláírást, mint secret-et, így a saját REST service tudni fogja, hogy ki használja a szolgáltatást.
Itt annyi bökkenő lehet, hogyha egy identity provider-en keresztül illetéktelenek férnek hozzá a rendszerhez, akkor meg vagyunk lőve. Le lehet tiltani magát az identity provider-t, de attól még ugyanúgy használni tudják az email címet és az aláírást ahhoz, hogy hozzáférjenek a szolgáltatásunkhoz. Ez ellen azt tudjuk tenni, hogy további adatokat fűzűnk a kérésekhez, amiket szintén aláírunk. Pl expiration time (hogy bizonyos idő után lejárjon, és újra be kelljen jelentkezni), random user specific code, random identity provider code, és így tovább. Így ha egy szolgáltatóval vagy egy fiókkal baj van, akkor a kódokat bármikor meg tudjuk változtatni, és így a régi aláírások már nem kerülnek elfogadásra többet. Az expiration-el meg pl localstorage-ben tárolt signature-ök lejárását szabályozhatjuk.

Nagyjából ugyanígy működik a dolog egy saját 3rd party klienssel is. Annyi a különbség, hogy a saját identity provider-nél a felhasználói név és a jelszó elkérése helyett egy ablakban az jelenik meg, hogy belépés Google account-tal, aztán ha az összejött, akkor lehet engedélyt adni a 3rd party kliensnek, hogy használja az alkalmazásunkat. Ami kisebb kényelmetlenség, hogy két engedély kérő ablak jelenik meg. Az egyik a Google-nél, hogy engedélyt ad e a felhasználó arra, hogy elkérje az oldalunk az email címét. A másik a saját ablak, ami a 3rd party klienssel kapcsolatban kér engedélyt a felhasználótól. Ebbe valószínűleg sokan belezavarodnának, viszont szerintem nem érdemes szétválasztani a regisztrációt és a beléptetést ilyen rendszerekben, mert bonyodalmakhoz vezet abban az esetben, ha több identity provider használatára is lehetőséget nyújtunk. Márpedig legalább kettőre szükség van, mert ha egy kiesne valamiért, attól még legalább a másikkal be tudnának lépni az emberek.

Nagyjából ennyit tudtam összerakni a témában. Szerintem érdekes, hogy ezeket az identity provider-eket láncba lehet fűzni, és egymásra hivatkozhatnak ugyanúgy, mint az SSL tanúsítványoknál, illetve, hogy egy REST service-nél is fel lehet használni őket azonosításra, nincs szükség a jelszó bekérésére és tárolására, elég csak az aláírás generálására használt secret-et megvédeni. Hogy a gyakorlatban ezt a megközelítést bárki használni fogja e, az a jövő zenéje. Mindenképp szükség van hozzá valamilyen egyszerűsítésre, vagy erősen szoktatni kell a felhasználókat az egymásra épített 3rd party kliensek koncepciójához.

Nincsenek megjegyzések:

Megjegyzés küldése