Przejdź do treści
DedicatedPHP Kontakt

Weryfikacja uprawnień w testach integracyjnych PHP

Przekształć reguły dostępu w scenariusze integracyjne sprawdzające uprawnienia, izolację między użytkownikami i brak nieautoryzowanych skutków ubocznych.

Macierz testów autoryzacji łącząca użytkowników, role i zasoby z dozwolonymi lub odrzuconymi wynikami

To, że dana osoba może się zalogować, nie oznacza, że jest uprawniona do odczytu lub modyfikacji wszystkich danych aplikacji. Uwierzytelnianie identyfikuje użytkownika, a autoryzacja określa, co może on zrobić, na jakim zasobie i w jakich warunkach. Trasa może wymagać prawidłowej sesji, a mimo to pozwalać komuś odczytać zamówienie należące do innego konta, jeśli nie jest sprawdzana relacja między użytkownikiem a zasobem.

Testy autoryzacji w PHP powinny weryfikować tę granicę za pośrednictwem punktów wejścia używanych przez aplikację, na przykład żądania HTTP kierowanego do trasy webowej lub API. Celem nie jest sprawdzanie, jak framework implementuje swoje polityki, lecz potwierdzenie obserwowalnego zachowania: kto może co zrobić, kiedy żądanie jest odrzucane i które dane pozostają niezmienione.

Przełóż reguły dostępu na macierz

Przełóż reguły dostępu na macierz — guía visual de DedicatedPHP

Zanim napiszesz testy, opisz reguły za pomocą czterech elementów: aktora, działania, zasobu i kontekstu. Kontekst obejmuje warunki wpływające na uprawnienia, takie jak przynależność do tej samej organizacji, bycie właścicielem rekordu lub określony stan. Taka struktura zapobiega sprowadzaniu autoryzacji do listy ról.

  • Aktor: użytkownik anonimowy, członek, osoba odpowiedzialna lub administrator.
  • Działanie: odczyt, utworzenie, edycja, usunięcie lub zatwierdzenie.
  • Zasób: zamówienie, dokument, konto lub inny chroniony obiekt.
  • Kontekst: właściciel, organizacja, stan zasobu lub aktualna relacja.

Na przykład reguła może zezwalać członkowi na przeglądanie własnych zamówień, a osobie odpowiedzialnej — zamówień swojej organizacji. Administrator może mieć szerszy dostęp, z zastrzeżeniem rzeczywistych reguł produktu. Macierz zamienia każde zdanie opisujące wymaganie biznesowe w sprawdzalne przypadki i uwidacznia brakujące kombinacje.

Nie trzeba testować wszystkich możliwych permutacji. Priorytetowo traktuj granice zaufania: użytkownika bez sesji, dwóch użytkowników z tej samej organizacji, użytkowników z różnych organizacji, osobę z uprzywilejowaną rolą oraz zasób, który do niej nie należy. Dodaj specjalne konteksty zmieniające decyzję, na przykład zarchiwizowane zamówienie, jeśli jego uprawnienia różnią się od uprawnień dotyczących aktywnego zamówienia.

Przygotuj reprezentatywne scenariusze integracyjne

Przygotuj dane w sposób jawny i oszczędny. Typowy test może utworzyć dwie organizacje, użytkownika w każdej z nich oraz zasób powiązany z jedną z organizacji. Następnie uwierzytelnij użytkownika próbującego uzyskać dostęp i wyślij żądanie do publicznej trasy aplikacji, używając identyfikatora zasobu. W ten sposób wspólnie testujesz punkt wejścia, uwierzytelnianie, autoryzację i odpowiedź.

Dla każdej istotnej reguły uwzględnij co najmniej jeden przypadek dozwolony i jeden odrzucony. Jeśli właściciel może edytować zasób, sprawdź, czy edycja z uprawnieniami działa i czy inny użytkownik nie może jej wykonać. Przypadek pozytywny jest niezbędny: zestaw testów, który sprawdza wyłącznie odrzucenia, może przejść, nawet jeśli aplikacja blokuje również użytkowników mających uprawnienia.

Sprawdź także nieistniejący zasób. Odpowiedź na nieznany identyfikator może różnić się od odpowiedzi na istniejący zasób, do którego użytkownik nie ma dostępu. W niektórych aplikacjach stosuje się odpowiedź „nie znaleziono”, aby nie ujawniać istnienia zasobu; inne informują, że dostęp jest zabroniony. Test powinien odzwierciedlać świadomie przyjętą politykę produktu, a nie narzucać uniwersalną konwencję.

Korzystaj z fabryk, konstruktorów obiektów fixture lub helperów testowych, które tworzą prawidłowe i czytelne stany. Unikaj zależności od stałych identyfikatorów, kolejności wykonywania testów lub współdzielonych danych, które inny test może zmodyfikować. Jeśli trwałość danych jest częścią zachowania, które chcesz sprawdzić, zweryfikuj wynik w bazie danych za pomocą narzędzi używanych w projekcie; nie zakładaj, że odpowiedź o powodzeniu sama w sobie gwarantuje prawidłowy stan.

Testuj izolację między użytkownikami i organizacjami

Izolacja zasługuje na osobne przypadki, ponieważ błędy często pojawiają się po zmianie identyfikatora w adresie URL lub treści żądania. Utwórz zasób należący do jednego konta i spróbuj go odczytać, edytować lub usunąć, korzystając z innej tożsamości. Powtórz sprawdzenie między organizacjami, jeśli aplikacja stosuje zakresy danych przypisane do firm. W przypadku krytycznych operacji testuj każde działanie: zabezpieczenie odczytu nie dowodzi, że chronione są również pobieranie, eksport lub aktualizacja.

Aby uniknąć powielania całego zestawu testów, współdziel wspólne przygotowanie i parametryzuj tylko wymiary wyrażające regułę. Przykładowo lista par aktor–zasób może określać, które kombinacje są dozwolone. Zachowuj konkretne nazwy przypadków: „członek innej organizacji nie może edytować zamówienia” wyjaśnia więcej niż nieprzejrzysty zestaw wartości logicznych. Rozdzielaj scenariusze, jeśli różnią się metodą, trasą lub oczekiwanymi skutkami.

Unikaj testowania każdej kombinacji ról z każdym zasobem, jeśli wiele z nich nie odzwierciedla odrębnych reguł. Zamiast tego zidentyfikuj uzasadnione równoważności i zachowaj jawne przypadki dla wyjątków oraz granic. Jeśli role są hierarchiczne, nie zakładaj, że jedna automatycznie dziedziczy wszystkie uprawnienia innej: sprawdź regułę faktycznie zdefiniowaną w produkcie.

Weryfikuj odpowiedzi i skutki uboczne

W przypadku odrzucenia sprawdź zarówno odpowiedź, jak i to, czy chroniona operacja nie została wykonana. Zależnie od interfejsu odpowiedzią może być przekierowanie, status informujący o braku uwierzytelnienia lub zakazie dostępu albo odpowiedź „nie znaleziono”. Sprawdzaj istotny kontrakt — status, format i, jeśli ma to znaczenie, komunikat — nie wiążąc testu ze zbędnymi szczegółami prezentacji.

W przypadku odrzuconej aktualizacji sprawdź, czy wrażliwe pola pozostały bez zmian. Przy usunięciu — czy rekord nadal jest dostępny. Jeśli operacja generuje fakturę, powiadomienie lub zdarzenie, sprawdź również, czy ten skutek nie wystąpił. Asercje powinny obejmować istotne dla biznesu skutki, a nie tylko kod HTTP.

Nieautoryzowane żądanie nie powinno też umożliwiać częściowych zmian. Jeśli przepływ obejmuje kilka operacji, sprawdź, czy odrzucenie następuje przed modyfikacjami albo czy transakcja pozostawia system w spójnym stanie. Nie trzeba w tym celu sprawdzać metod prywatnych: obserwuj odpowiedź oraz zapisane dane lub skutki.

Dbaj o odporność testów na zmiany implementacji

Testy integracyjne powinny korzystać ze stabilnego interfejsu aplikacji, na przykład trasy i żądania z tożsamością uwierzytelnioną za pomocą dostępnych mechanizmów testowych. Unikaj bezpośredniego wywoływania wewnętrznej klasy polityk, jeśli chcesz zweryfikować rzeczywisty dostęp do zasobu: może to pominąć rejestrację trasy, middleware lub sposób wczytywania obiektu.

Nie przekształcaj przy tym zestawu testów w kopię całej aplikacji. Testuj kontrakt autoryzacji w reprezentatywnych punktach, a testy jednostkowe zostaw dla czystych reguł wymagających wielu przypadków kontekstowych. Takie połączenie ułatwia lokalizowanie błędów: test reguły może wyizolować warunek, a test integracyjny potwierdza, że reguła chroni udostępniony przepływ.

Gdy zmieniają się role, trasy lub polityki, przed modyfikacją oczekiwań ponownie przejrzyj macierz. Test aktualizowany wyłącznie po to, by akceptował nowy wynik, może ukryć przypadkowe rozszerzenie uprawnień. Zapisz powód zmiany reguły, ustal, którzy aktorzy zyskują lub tracą dostęp, i dodaj przypadki chroniące nowe granice.

Lista kontrolna do przeglądu zmiany

Lista kontrolna do przeglądu zmiany — guía visual de DedicatedPHP
  • Czy zdefiniowano aktora, działanie, zasób i kontekst?
  • Czy dla zmienianej reguły istnieje co najmniej jeden scenariusz dozwolony i jeden odrzucony?
  • Czy w razie potrzeby testowany jest dostęp między użytkownikami lub zakresami danych?
  • Czy uwzględniono nieistniejący zasób i wybraną politykę nieujawniania informacji?
  • Czy żądanie przechodzi przez punkt wejścia, który powinien być chroniony?
  • Czy sprawdzono, że odrzucenie nie zmienia danych ani nie powoduje skutków ubocznych?
  • Czy dane testowe są odizolowane, czytelne i możliwe do odtworzenia?
  • Czy oczekiwania odzwierciedlają decyzję dotyczącą produktu, a nie przypadkowy szczegół frameworka?

Przydatny zestaw testów nie dowodzi abstrakcyjnie, że „istnieją uprawnienia”: pokazuje, że istotne kombinacje działają, a nieautoryzowane nie przekraczają granicy. Utrzymywanie tej macierzy razem z konkretnymi testami integracyjnymi ułatwia przegląd zmian dostępu bez wiązania bezpieczeństwa z określoną wewnętrzną implementacją.

Chcesz zastosować te pomysły w swoim projekcie?Omówmy Twoją platformę PHP.
Zobacz powiązaną usługę