Wprowadzanie hasła w wywołaniu interfejsu API REST


31

Załóżmy, że mam interfejs API REST, który służy również do ustawiania / resetowania haseł. Załóżmy również, że działa to w przypadku połączeń HTTPS. Czy jest jakiś dobry powód, aby nie umieszczać tego hasła w ścieżce wywołania, powiedzmy też, że zakoduję je w BASE64?

Przykładem może być zresetowanie hasła takiego:

http://www.example.com/user/joe/resetpassword/OLDPASSWD/NEWPASSWD

Rozumiem, że BASE64 nie jest szyfrowaniem, ale w tym przypadku chcę tylko zabezpieczyć hasło do surfowania po ramieniu.


61
Sugerujesz efekty uboczne GET? To właśnie narusza protokół.
Esben Skov Pedersen

27
To nie jest tak naprawdę REST, ponieważ resetpassword/OLDPASSWD/NEWPASSWDnie jest zasobem. To wywołanie procesu. Nie musisz umieszczać wszystkiego w adresie URL.
usr

5
@Esben: kto powiedział, że to GET? OP nigdy tego nie powiedział.
Dagnelies

3
To prawda, że ​​nie pytał. Ale jego komentarz do odpowiedzi Netcha mówi „Zgadnij, że w końcu będę musiał użyć testu POST”, więc możemy założyć, że pierwotnie zamierzał / zapytał o GET. Co, jak zauważa Esben, jest złą rzeczą. GET powinien tylko do odczytu.
Mawg,

Odpowiedzi:


76

Dobry serwer rejestruje wszystkie wysłane do niego żądania, w tym adresy URL (często bez części zmiennej po „?”), Źródłowy adres IP, czas wykonania ... Czy naprawdę chcesz, aby ten dziennik (potencjalnie czytany przez szeroką grupę administratorów) zawierał krytycznie bezpieczne informacje jako hasła? Base64 nie stanowi dla nich przeszkody.


42
To nie jest największy powód korzystania z POST. To powód bezpieczeństwa. Ale jak Esben już zauważa w komentarzach, zmiana stanu za pomocą GET jest naruszeniem takiej usługi odpoczynku
Pinoniq

2
@BartFriederichs: i historia przeglądarki zapamięta adres URL. I wypróbowanie anonimowego zestawu haseł przez utworzenie strony internetowej zawierającej link do wszystkich haseł, które chcesz wypróbować, i umożliwienie Googlebotowi wykonania rzeczywistych żądań ...
RemcoGerlich

2
„Ale jak Esben zauważa już komentarze, zmiana stanu za pomocą GET stanowi naruszenie takiej usługi odpoczynku” Zauważyłem również ten komentarz, ale nie widzę, gdzie ktoś mówi, że to była prośba GET. W końcu możesz osadzić informacje w identyfikatorze URI i mimo to POST. Jednak tak naprawdę nie jest to RESTful , ponieważ URI tak naprawdę nie nazywa zasobu.
Joshua Taylor

Cześć, dobra odpowiedź, ale nie zgadzam się z „bez części zmiennej po„? ”” ... istnieje wiele takich, które przechowują pełny adres URL !!!
Dagnelies

2
„zmiana stanu za pomocą GET stanowi naruszenie takiej usługi odpoczynku” - Nie dajmy się zbytnio wciągnąć w REST. Zmiana stanu za pomocą GET stanowi naruszenie protokołu HTTP .
Dan Ellis,

69

To, co proponujesz, nie jest ani bezpieczne, ani RESTful.

@Netch wspomniał już o problemie z logami, ale jest też inny problem polegający na tym, że wyświetlasz hasła wysyłane przez HTTP, dzięki czemu przechwytywanie haseł za pomocą dowolnego podsłuchu lub ataku typu man-in-the-trywialnego jest banalne.

Kiedy wykonujesz żądanie GET przy użyciu REST, różne elementy w adresie URL reprezentują elementy o drobniejszych ziarnach. Twój adres URL brzmi, jakbyś zwrócił NEWPASSWD część OLDPASSWD, która jest częścią hasła resetowania. To nie ma żadnego sensu semantycznego. GET nie powinny być używane do zapisywania danych.

Powinieneś robić coś takiego:

POST https://www.example.com/user/joe/resetpassword/
{oldpasswd:[data], newpasswd:[data]}

POST, ponieważ piszesz dane, a https, ponieważ nie chcesz, aby były wąchane.

(To naprawdę niskie bezpieczeństwo. Absolutne minimum, które powinieneś zrobić.)


2
Czy nie oznaczałoby to zaszyfrowania haseł po stronie klienta? Czy to jest zalecane?
Rowan Freeman

1
Uwaga: nie pozwoli to na wymuszanie wymagań dotyczących hasła (długości itp.). W twoim przypadku może to nie stanowić problemu, chociaż jest to częsta praktyka bezpieczeństwa i czasami wymagana przez niektóre podmioty.
Paul Draper

8
Nie tworzysz nowego rekordu, ale aktualizujesz istniejący (zwykle), więc powinien to być PUT zamiast POST.

4
To nie jest bardzo RESTful. resetpassword nie jest zasobem, a tym bardziej zasobem podrzędnym. Jednak /user/joe/passwordjest trochę lepiej, ale nie optymalne.
whirlwin

12
@CamilStaps Nie, nie możesz używać PUT, ponieważ PUTjest idempotentny. Ale jeśli hasło zostanie zmienione z secretna supersecretpomyślnie, to samo żądanie nie powiedzie się za drugim razem, więc POSTtutaj jest poprawne. Oczywiście, jak powiedział @whirlwin, ten zasób nie jest dobrze nazwany.
Residuum

60

Proponowany program ma problemy w kilku obszarach.

Bezpieczeństwo

Ścieżki URL są często rejestrowane; stawianie niepasujących haseł na ścieżce to zła praktyka.

HTTP

Informacje o uwierzytelnieniu / autoryzacji powinny pojawić się w nagłówku autoryzacji. Lub potencjalnie, w przypadku rzeczy opartych na przeglądarce, nagłówek Cookie.

RESZTA

Czasowniki, takie jak resetpasswordw adresie URL, są zazwyczaj wyraźnym znakiem niereprezentatywnego paradygmatu transferu stanu. Adres URL powinien reprezentować zasób. Co to znaczy GET resetpassword? Lub USUŃ?

API

Ten schemat wymaga zawsze znajomości poprzedniego hasła. Prawdopodobnie będziesz chciał pozwolić na więcej przypadków; np. hasło zostało utracone.


Możesz użyć uwierzytelniania podstawowego lub szyfrowanego , które są dobrze zrozumiałymi schematami.

PUT /user/joe/password HTTP/1.0
Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
Content-Type: text/plain
Host: www.example.com

NEWPASSWD

Nie umieszcza ultra wrażliwych informacji na ścieżce i postępuje zgodnie z konwencjami HTTP i REST.

Jeśli musisz zezwolić na inny tryb autoryzacji (np. Token wysłany przez zweryfikowany kanał w celu zresetowania hasła), możesz po prostu użyć innego nagłówka autoryzacji bez konieczności zmiany czegokolwiek innego.


4

Poza bezpieczeństwem problem polega na tym, że nie jest to podejście RESTful.

OLDPASSWDi NEWPASSWDnie oznacza niczego w hierarchii zasobów, a co gorsza, operacja nie jest idempotentna.

Możesz więc używać tylko POSTjako czasownika i nie powinieneś dołączać dwóch haseł do ścieżki zasobów.


1
Nie tworzysz nowego rekordu, ale aktualizujesz istniejący (zwykle), więc powinien to być PUT zamiast POST.

2
@CamilStaps Gdyby tylko ustawiał hasło, mogłoby być. Ale ponieważ przypuszczalnie stare hasło również musi zostać zweryfikowane, sprawia, że ​​operacja nie jest idempotentna, a zatem PUTjest dyskwalifikowana jako czasownik. Można z niego korzystać, PUTale w obecnej formie tak nie jest.
biziclop

OLDPASSWD to informacje uwierzytelniające i w ogóle nie powinny znajdować się w adresie URL.

Niekoniecznie powszechną praktyką jest jawne żądanie podania starego hasła oprócz uwierzytelnienia.
biziclop

3

Problem polega na unikaniu haseł w postaci zwykłego tekstu w żądaniach. Istnieją dwie opcje spełnienia restrykcyjnych wymagań usługi internetowej.

1. Hashowanie po stronie klienta

  • Domyślam się, że przechowujesz swoje hasła, takie jak np. Hash (hasło + sól)
  • Możesz haszować nowe hasło solą po stronie klienta
  • Oznacza to: Utwórz nową sól po stronie klienta, utwórz hash, np. Hash (newPassword + newSalt)
  • Wyślij nowo utworzony skrót i sól do spokojnej usługi internetowej
  • Wyślij stare hasło również jako skrót (oldPassword + oldSalt)

2. Szyfrowanie

  • Utwórz zasób „klucza jednorazowego” (otk) dla użytkownika takiego jak / otk / john
  • Ten zasób zwraca bezpieczny losowy unikalny klucz jednorazowy, np. KbDlJbmNmQ0Y0SmRHZC9GaWtRMW0ycVJpYzhMcVNZTWlMUXN6ZWxLdTZESFRs i unikalny identyfikator, np. 95648915125
  • Twoja spokojna usługa internetowa musi przechowywać ten przypadkowy otk do następnej bezpiecznej komunikacji z ID 95648915125
  • Zaszyfruj swoje nowe i stare hasło za pomocą otk, np. AES (ze względów bezpieczeństwa powinieneś użyć dwóch oddzielnych otków dla starego i nowego hasła)
  • Wyślij zaszyfrowane hasła do zasobu zmiany hasła o identyfikatorze 95648915125
  • Jedna kombinacja otk i ID może działać tylko raz, więc musisz usunąć tę kombinację po zmianie hasła
  • Możliwa lepsza opcja: Wyślij bieżące / stare hasło przez Basic-Auth.

Uwaga: HTTPS jest wymagany dla obu opcji!


1
Dlaczego miałbym podwójnie szyfrować (twój schemat szyfrowania za pomocą OTK i HTTPS) wymianę haseł? Jaki wektor ataku nie jest objęty HTTPS?
Bart Friederichs,

1
Jedynym celem jest uniknięcie możliwych dzienników serwera. Treść żądania HTTP (na przykład z nowym hasłem w postaci zwykłego tekstu) może być również zarejestrowana, chociaż używany jest protokół HTTPS. Innym możliwym atakiem jest użycie samopodpisanego certyfikatu, który jest szczególnie wykorzystywany do celów wewnętrznych.
maz258,

2

Jakie są funkcje operacji resetowania hasła?

  1. To coś zmienia.
  2. Istnieje wartość, która jest ustawiona.
  3. Tylko niektóre osoby mogą to robić (użytkownik, administrator lub jedno lub drugie, być może z różnymi zasadami, jak to zrobić).

Punkt 1 tutaj oznacza, że ​​nie możesz użyć GET, musisz albo POST wysłać coś reprezentującego operację zmiany hasła do identyfikatora URI reprezentującego zasób, który obsługuje zmiany hasła, lub PUT coś reprezentującego nowe hasło do identyfikatora URI reprezentującego hasło lub reprezentującego coś (np. użytkownika), którego hasło jest funkcją.

Zazwyczaj POST, zwłaszcza dlatego, że może być niewygodne WYKŁADANIE czegoś, czego nie możemy później uzyskać i oczywiście nie możemy uzyskać hasła.

Punkt 2 będzie zatem danymi reprezentującymi nowe hasło, w tym, co jest POST.

Punkt 3 oznacza, że ​​będziemy musieli autoryzować żądanie, co oznacza, że ​​jeśli użytkownik jest bieżącym użytkownikiem, będziemy potrzebować aktualnego hasła, aby nam je udowodnić (choć niekoniecznie otrzymamy aktualne hasło, jeśli np. Wyzwanie oparte na haszowaniu służyło do udowodnienia znajomości go bez wysyłania go).

URI powinien zatem być podobny do <http://example.net/changeCurrentUserPassword>lub <http://example.net/users/joe/changePassword>.

Możemy zdecydować, że chcemy otrzymać aktualne hasło w danych POST, a także w ogólnym mechanizmie autoryzacji.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.