Open PrzemyslawLukanowski opened 4 years ago
Mam pytanie odnośnie kodowania Base64Url vs Base64, w/g dokumentacji należy po zakodowaniu Base 64 usunąć znaki końcowe "=", zamienić znaki "+" na "-" i "/" na "_" (taka specyfikacja Base64Url), jednak w waszym algorytmie kiedy usunę końcowe znaki "=" serwer zwraca błąd. Więc pytanie odnośnie znaków końcowych, czy poprawicie to aby było jak w dokumentacji czy nie należy się tym przejmować?
Czy code_verifier (KnAijeNvdSeloYlVcOh3HRmgZX57wDeVHiwRFQKO2F9DdBI) na pewno jest poprawnie zakodowany w Waszym przykładowym url, gdzie code_challenge
ma wartość a69se03ZmsPhTLYQKHpGUH7m5waf-U8D-5pTwFRgLI4=
?
Chodzi mi o ostatni znak =
w wartości code_challenge
.
W Waszej dokumentacji napisane jest, że:
Dla S256: code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
, więc w zasadzie przy algorytmie Base64url, na końcu wynikowego ciągu nie powinno być znaku =
.
Może Pan to wyjaśnić?
Hmm zastanawia mnie jedno. W jaki sposób to zabezpieczenie ma chronić przed złośliwym oprogramowaniem ? Załóżmy, że dodam parametr code_challenge z maksymalną długością 128 znaków oraz code_challenge_method z wartością S256. Super, wszystko gra, mamy zabezpieczoną aplikacje, ale przecież to nie zmienia faktu, że złośliwe oprogramowanie może przecież te parametry kompletnie pominąć. Wystarczy przecież że gdzieś w tle wykorzysta opcję cichego uwierzytelniania (prompt=none) i będzie się ścigać z właściwą aplikacją, aby zamienić jednorazowy kod na access token. No i oczywiście może to robić do oporu. Nie sprawdzałem tego jeszcze w praktyce, na razie przyszedł mi do głowy taki scenariusz, także stąd się rodzi pytanie. Czy nie oznaczacie czasem jakoś aplikacji dodatkową flagą, aby wymuszać podawanie obu parametrów ? Zabezpieczenie tylko w takim przypadku wydaje się mieć sens
A przepraszam, zapomniałem całkiem że aby zamienić code na tokeny to wymagana jest autoryzacja przy pomocy client_id oraz client_secret które dostajemy przy rejestracji aplikacji. Rozumiem więc że to kolejna warstwa zabezpieczeń choć nie potrafię sobie wyobrazić scenariusza w jaki sposób złośliwe oprogramowanie byłoby w stanie cokolwiek tutaj zdziałać bez nich. Niemniej jednak nadal zastanawiam się co daje to rozwiązanie jeśli można te parametry kompletnie pominąć nawet jak aplikacja z nich korzysta
@Gerston @atmdevnet Zgadza się, znak =
w wartości code_challenge
powinien być usunięty. W najbliższym czasie wprowadzimy poprawkę, zmienimy wtedy przykład w newsie oraz w poradniku.
@kormichu Według tego mechanizmu, jeżeli przekażesz dwa dodatkowe parametry, to - mimo że złośliwe oprogramowanie będzie w stanie przejąć authorization code - na etapie żądania o token w odpowiedzi na request otrzyma:
{
"error": "invalid_grant",
"error_description": "code_verifier is missing."
}
Poniżej wykres, który obrazuje działanie tego mechanizmu - złośliwe oprogramowanie odbije się na punkcie 5.
@PrzemyslawLukanowski ok widziałem w dokumentacji że ten parametr jest wymagany bo inaczej odbije się złośliwa aplikacja w pkt 5):
WAŻNE! Jeśli w pkt.1 zastosowałeś mechanizm PKCE, to parametr code_verifier jest parametrem wymaganym. Podaj tę samą wartość, jaką określiłeś wcześniej, np.
jednak bardziej mi chodzi o następującą sytuacje: 1) złośliwe oprogramowanie wykrywa że użytkownik próbuje się autoryzować na adres https://allegro.pl/auth/oauth/authorize z wykorzystaniem code_challenge 2) użytkownik się autoryzuje, pierwotna aplikacja normalnie dostaje dostęp, złośliwe oprogramowanie z racji tego że code_challenge występuje nie próbuje nawet wykorzystać code który aplikacja dostaje w zwrotce 3) następnie złośliwe oprogramowanie robi jeszcze raz request na adres https://allegro.pl/auth/oauth/authorize tym razem pomijając parametry związane z code_challenge i przekazując dodatkowo parametr prompt=none. Dodatkowo modyfikuje nieznacznie adres w parametrze redirect_uri aby nie musiał się ścigać z aplikacją pierwotną o dostęp. Wystarczy tak naprawdę że zostanie sama domena
Oczywiście atak jest niemożliwy jeśli nie wyciekł client_secret z aplikacji bo client_id jest przecież jawny. Pytając o wymagalność parametrów miałem na myśli to aby były one wymagane na każdym etapie autoryzacji, a nie tylko końcowej w pkt 5)
@Gerston @atmdevnet Wdrożyliśmy odpowiednią zmianę.
@kormichu Weryfikuję opisany przez Ciebie scenariusz, wrócę z odpowiedzią.
Aby odpowiedzieć na to pytanie, należy rozważyć różne sposoby użycia authorization flow w zależności od kontekstu:
Aplikacje mobilne Aplikacje mobilne charakteryzują się custom uri - czyli uri, które aplikacje mogą oznaczyć jako swój callback. Niestety kilka aplikacji może podpiąć się do tego samego custom uri, przez co złośliwa aplikacja może w prosty sposób przechwycić authorization_code. Dzięki PKCE uodparniamy się na wykorzystanie przechwyconego authorization_code, a złośliwa aplikacja, próbując wymienić authorization_code na token dostanie błąd dotyczący braku code_verifier. Ogólnie aplikacje mobilne jako software działający po stronie użytkownika są wrażliwe na dekompilację i ratować można się jedynie security by obscurity. W ostatecznym rozrachunku jednak client_id i client_secret zaszyte w aplikacji nie mogą być uznawane za bezpieczne i trzeba liczyć się z pozyskaniem ich przez osoby niepożądane.
Aplikacje desktop Aplikacje desktop pozwalają tylko na jedną aplikację nasłuchującą na danym porcie, dzięki czemu złośliwa aplikacja nie może podpiąć się pod to samo URI. Bardziej wyrafinowanym sposobem może jednak sniffować pakiety http i w ten sposób pozyskać authorization_code. Dalej proces wygląda dokładnie tak samo, jak w aplikacjach mobilnych.
Aplikacje web Aplikacje web są odporne na ataki, które zabezpiecza PKCE, ponieważ authorization_code nie wraca do naszego systemu a jest wysyłany do backendu aplikacji web. Co ważniejsze client_id i client_secret są bezpiecznie zaszyte w backendzie aplikacji i nie mogą być w żaden “prosty” sposób pozyskanie.
Wdrożenie PKCE to nasz pierwszy krok na długiej drodze podnoszenia standardów security oauth2. W najbliższym czasie mamy w planie:
dzień dobry, czy jest możliwość dodania do wywołania redirect_uri dodatkowego parametru identyfikującego sesję, podczas autoryzacji aplikacji oauth typu web (code flow) ? Mam do czynienia z takim scenariuszem autoryzacji, w którym oprócz serwisu allegro jest jeszcze własny , oddzielny web serwer autoryzacji (rest api) oraz aplikacja desktop klienta. W tym scenariuszu, użytkownik dostaje z mojego serwera uri do autoryzacji, następnie wykorzystuje je do autoryzowania aplikacji w allegro, po czym do mojego serwera kierowane jest wywołanie redirect_uri z kodem. Na serwerze mam dostępny tylko ten kod przekazany z allegro i pole referer z nagłówka wywołania. Natomiast, nie widzę w tym scenariuszu możliwości zidentyfikowania sesji użytkownika. Gdyby, oprócz kodu potrzebnego do uzyskania tokena byłby przekazany wprost jeszcze dodatkowy parametr, np. wysłany wcześniej code_challenge, to ułatwiłoby identyfikację sesji użytkowników. Na polu referer nagłówka requesta, skąd teoretycznie można wyciągnąć code_challenge, raczej nie można polegać ponieważ przeglądarka może nie uwzględnić pola referer. Chodzi mi głównie o to, że chcąc skorzystać z mechanizmu PKCE muszę wcześniej, zanim odczytam id użytkownika allegro z wygenerowanego tokena, skojarzyć code_verifier z utworzonym na początku tej całej procedury code_challenge.
Ja bym to zrobił tak, że do redirect_uri doklejałbym UUID, który byłby powiązany z konkretnym kontem. Scenariusz wyglądałby mniej więcej tak: 1) klient klika na button do połączenia z Allegro 2) wewnętrznie generowane jest nowe konto i przypisywany jest do niego UUID 3) na podstawie danego konta generowany jest url do autoryzacji oauth https://allegro.pl/auth/oauth/authorize gdzie w parametrze redirect_uri doklejony jest wygenerowany UUID z kroku 2) 4) użytkownik autoryzuje się w Allegro i jest przekierowany na adres w urlu 5) na postawie UUID pobieramy konkretne konto z bazy
@atmdevnet Czy powyższy scenariusz rozwiązuje Twój problem?
@PrzemyslawLukanowski scenariusz na który wskazałeś opisuje bardziej pewne aspekty techniczne procesu po stronie serwera autoryzacji, który nie jest zgodny z regułą autoryzacji dla redirect_uri (cytat z poradnika):
redirect_uri
: Adres do przekierowania, na który wysłany zostanie kod (musi być zgodny z tym podanym przy rejestracji aplikacji)
Myślę, że najprostszym rozwiązaniem byłoby wywołanie redirect_uri ze skopiowanym code_challenge z requesta do allegro. Czy moglibyście rozważyć ten problem, który opisałem wyżej?
Reguła jest taka, aby adres podany w redirect_uri zaczynał się od tego który jest podany przy rejestracji aplikacji, sprawdzone empirycznie
@PrzemyslawLukanowski tak się zastanawiam, czy czasem dodanie code_challenge co redirect_uri nie popsułoby mechanizmu PKCE w kontekście aplikacji mobilnych jeśli parametr code_challenge_method ustawiony byłby na plain. Atakujący miałby dostęp do wszystkich potrzebnych danych
Mechanizm PKCE w przypadku kiedy wykonujecie request na swój authorization server mija się z celem, ponieważ w tym przypadku złośliwe oprogramowanie nie przechwyci authorization_code (pod warunkiem, że serwer nie jest on zawirusowany). Możecie wykorzystać parametr “state”, który w najbliższej przyszłości udokumentujemy - pomoże on Wam zidentyfikować zwrócony request - to może być rozwiązaniem Waszego problemu.
ok, bardzo dziękuję
@kormichu W tym przypadku masz rację - jeśli korzystasz z code_challenge z code_challenge_method=plain, wyciek code_challenge jest równoznaczny z dostępem do code_verifier, mechanizm PKCE nie przyniesie korzyści.
Udostępniliśmy mechanizm PKCE (Proof Key for Code Exchange), który możesz użyć podczas autoryzacji użytkownika. Dzięki niemu zabezpieczysz swoją aplikację przed wykorzystaniem kodu autoryzacyjnego (authorization code) przez złośliwe oprogramowanie.
Aby z niego skorzystać, wygeneruj we własnym zakresie code_verifier - musi to być losowy ciąg znaków o długości pomiędzy 43 a 128 znaków, użyjesz go podczas żądania o token. Następnie w procesie autoryzacji dodaj dwa parametry:
Poniżej znajdziesz przykłady żądań HTTP dla code_verifier = KnAijeNvdSeloYlVcOh3HRmgZX57wDeVHiwRFQKO2F9DdBI.
Dla code_challenge_method=S256:
https://allegro.pl/auth/oauth/authorize?response_type=code&client_id=a21...6be&redirect_uri=http://exemplary.redirect.uri&code_challenge_method=S256&code_challenge=a69se03ZmsPhTLYQKHpGUH7m5waf-U8D-5pTwFRgLI4
Dla code_challenge_method=plain:
https://allegro.pl/auth/oauth/authorize?response_type=code&client_id=a21...6be&redirect_uri=http://exemplary.redirect.uri&code_challenge_method=plain&code_challenge=KnAijeNvdSeloYlVcOh3HRmgZX57wDeVHiwRFQKO2F9DdBI
Następnie, na etapie żądania o token, jako code_verifier przekaż tę samą wartość, jaką określiłeś wcześniej:
curl -X POST -H 'Authorization: Basic YTI...Hg=' 'https://allegro.pl/auth/oauth/token?grant_type=authorization_code&code=pOPEy9Tq94aEss540azzC7xL6nCJDWto&redirect_uri=http://exemplary.redirect.uri&code_verifier=KnAijeNvdSeloYlVcOh3HRmgZX57wDeVHiwRFQKO2F9DdBI'
Więcej informacji znajdziesz w naszym poradniku - autoryzacja użytkownika.
We have introduced PKCE (Proof Key for Code Exchange) mechanism, which you can use in the user authorization process. Owing to the PKCE, you can protect your application against the use of authorization_code by malicious software.
To use it, generate code_verifier by yourself - it should be a random string containing between 43 and 128 characters, which you will use in access token request. Then in the authorization process, add two parameters:
Below you can find examples of HTTP requests for code_verifier = KnAijeNvdSeloYlVcOh3HRmgZX57wDeVHiwRFQKO2F9DdBI.
For code_challenge_method=S256:
https://allegro.pl/auth/oauth/authorize?response_type=code&client_id=a21...6be&redirect_uri=http://exemplary.redirect.uri&code_challenge_method=S256&code_challenge=a69se03ZmsPhTLYQKHpGUH7m5waf-U8D-5pTwFRgLI4
For code_challenge_method=plain:
https://allegro.pl/auth/oauth/authorize?response_type=code&client_id=a21...6be&redirect_uri=http://exemplary.redirect.uri&code_challenge_method=plain&code_challenge=KnAijeNvdSeloYlVcOh3HRmgZX57wDeVHiwRFQKO2F9DdBI
Then, when you’re requesting for access token, as code_verifier provide the same value as you specified earlier:
curl -X POST -H 'Authorization: Basic YTI...Hg=' 'https://allegro.pl/auth/oauth/token?grant_type=authorization_code&code=pOPEy9Tq94aEss540azzC7xL6nCJDWto&redirect_uri=http://exemplary.redirect.uri&code_verifier=KnAijeNvdSeloYlVcOh3HRmgZX57wDeVHiwRFQKO2F9DdBI'
You will find more information in our guide - user authorization.