Pracuję nad interfejsem API dla usługi REST, którą zamierzam zarówno produkować, jak i konsumować. Ostatnie kilka dni spędziłem, próbując wymyślić, jak ładnie obsługiwać uwierzytelnianie, i myślę, że w końcu coś wymyśliłem.
Wymyślam to na podstawie następujących faktów na temat stosu aplikacji:
- Klient i serwer są w .NET4 (część klienta w profilu klienta)
- Serwer udostępnia za pomocą usługi WCF REST
- Naprawdę nie chcę przechowywać nazwy użytkownika i hasła w pamięci aplikacji
Od 3 chciałem użyć formy uwierzytelniania tokena, aby po zweryfikowaniu poświadczeń przez serwer klient otrzymał token z powrotem do użycia w pozostałej części aplikacji (pozwoli mi to robić inne rzeczy, takie jak przekroczenie limitu czasu użytkowników, możliwość płynnego przenoszenia użytkowników między wersjami internetowymi i komputerowymi itp.). Po zastanowieniu się, jak sprawić, by połączenia były odtwarzane ponownie i były odporne na manipulacje, wymyśliłem następujące rzeczy:
- Zanim klient podejmie próbę uwierzytelnienia, generuje parę kluczy Diffie-Hellman przy użyciu
ECDiffieHellmanCng
klasy. - Wysyła publiczną część pary kluczy przewodowo wraz z nazwą użytkownika i hasłem (oczywiście przez HTTPS).
- Serwer uwierzytelnia kombinację nazwy użytkownika i hasła, jeśli się powiedzie, wykonuje następujące czynności:
- Tworzy unikalny token sesji
- Generuje własną parę kluczy DH i oblicz wspólny klucz tajny na podstawie klucza publicznego dostarczonego przez klienta
- Odnotowuje w swojej bazie danych token sesji, wspólny klucz tajny, użytkownika i czas „ostatniej akcji” (używany w przypadku ruchomego okna wygaśnięcia)
- Zwraca token sesji, jego publiczny klucz DH i komunikat o powodzeniu uwierzytelnienia
- Klient bierze klucz DH z odpowiedzi, oblicza wspólny klucz tajny i przechowuje zarówno token, jak i klucz tajny w pamięci.
Od tego momentu kombinacja token / sekret sesji działa jak większość innych interfejsów API REST, z żądaniem odczytywanym odciskiem palca i znacznikiem czasu, a następnie generowanym przez niego HMAC. Za każdym razem, gdy klient wykonuje akcję przeciwko serwerowi, sprawdza parę token / klucz tajny i zezwala na akcję, jeśli jest ważna i nie wygasła, i aktualizuje ostatni rekord akcji w sesji.
Nie widzę żadnych oczywistych wad i prawdopodobnie jestem w tym celu przeprojektowany, ale w pewnym momencie muszę się tego nauczyć. HMAC zapobiega atakom powtórkowym, negocjacje DH pomagają zapobiegać atakom MITM (nie mogę sobie wyobrazić wykonalnego ataku z góry mojej głowy między HMAC / DH).
Jakieś dziury, które każdy może w to wbić?