Przejdź do treści
Powrót do bloga

Opublikowano 26 sierpnia 2026 · 3 min czytania

Policzyłem, ile tras w moim API jest naprawdę chronionych. Grep po pliku kłamał

Mój panel ma dziś ponad 240 tras API. Każda z nich zaczyna się od tej samej linijki:

if (!(await isAuthed())) return new Response(null, { status: 401 });

Nie ma warstwy, która robiłaby to za mnie. W tym frameworku katalog /api jest domyślnie poza zasięgiem pośrednika, więc cała kontrola dostępu to powtórzenie tej jednej linijki w każdym pliku z osobna. Zapomnienie jej nie daje żadnego objawu: kod się kompiluje, panel działa, a trasa stoi otworem dla każdego, kto zna adres.

Postanowiłem policzyć, ile ich naprawdę jest.

Pierwsza odpowiedź była fałszywa

Odruch: policzyć pliki, które zawierają isAuthed, i porównać z liczbą plików tras. Wyszło ładnie — prawie wszystkie.

Prawie. Zacząłem oglądać te, które „prawie” tworzyły, i trafiłem na coś takiego:

// Trasa PUBLICZNA — celowo bez isAuthed(): otwiera ją klient
// linkiem z maila, więc nie ma tu sesji do sprawdzenia.

Plik wspominał isAuthed. W komentarzu. Uzasadniającym, dlaczego go nie ma.

Takich miejsc było pięć. Każde z nich moje pierwsze liczenie zaliczyło jako „chronione”, bo szukałem ciągu znaków w pliku, a nie odpowiadałem na pytanie, które naprawdę mnie interesowało.

Drugi błąd: plik to nie trasa

Nawet po wyrzuceniu komentarzy liczenie po plikach nie ma sensu, bo jeden plik może obsługiwać kilka metod HTTP. Typowa trasa wygląda tak:

export async function GET()  { … }
export async function PATCH(){ … }
export async function DELETE(){ … }

Jeżeli sprawdzenie jest w GET, ale ktoś dopisał później DELETE i zapomniał — plik nadal „zawiera isAuthed”. Wyszukiwarka mówi: chronione. Rzeczywistość: skasować rekord może każdy.

Właściwą jednostką liczenia jest uchwyt HTTP, nie plik. Dopiero taki rachunek odpowiada na pytanie „czy każde wejście do systemu jest zamknięte”.

Jak to policzyłem w końcu

Wypisałem wszystkie eksportowane funkcje GET/POST/PATCH/PUT/DELETE w katalogu tras, a potem dla każdej sprawdziłem, czy sprawdzenie sesji pada w jej ciele, a nie gdziekolwiek w pliku. Do tego osobna lista tras, które są publiczne z założenia — akceptacja oferty przez klienta, odbiór dokumentu z linku, formularz kontaktowy — każda z krótkim uzasadnieniem, dlaczego wolno jej stać otworem.

Efekt jest nudny i o to chodzi: dwie listy, które da się przejrzeć w pięć minut, i pytanie „ile tras jest niechronionych” ma odpowiedź liczbową zamiast wrażenia.

Co z tego wynika ogólniej

Wyszukiwanie tekstu odpowiada na pytanie o tekst, nie o zachowanie. To brzmi banalnie i właśnie dlatego jest groźne: wynik wygląda na pomiar, a jest tylko liczbą trafień. Za każdym razem, gdy z takiego szukania wyciągam wniosek, muszę sobie zadać pytanie, czy to, co policzyłem, jest tym, o co pytałem.

Bezpieczeństwo oparte na powtarzaniu tej samej linijki jest bezpieczeństwem opartym na pamięci. Docelowo to powinna być bariera, przez którą nie da się przejść przez zapomnienie. Na dziś mam listę i test, który ją sprawdza — świadomie mniej niż bariera, ale sporo więcej niż nadzieja.

Publiczne bywa poprawne — pod warunkiem, że jest napisane. Pięć tras stoi otworem celowo. Różnica między świadomym wyjątkiem a dziurą polega wyłącznie na tym, że ktoś zapisał, który to który. Nieudokumentowany wyjątek po pół roku jest nie do odróżnienia od pomyłki — nawet dla autora.


To był fragment przeglądu, który zrobiłem sobie sam, na własnym systemie, bez zewnętrznego audytora. Nie twierdzę, że jest kompletny — twierdzę, że po nim wiem, ile tras mam i które z nich są otwarte, a przed nim tylko mi się wydawało.

Patryk Piecyk

Patryk Piecyk

Warszawa · junior konsultant wdrożeniowy · dostępny od zaraz

Przez 7,5 roku pracowałem w niemieckiej firmie, pięć lat prowadząc jej biuro: zamówienia, faktury, reklamacje, ERP. Od czerwca 2026 buduję własne narzędzia do tej samej roboty — nie jestem programistą z wykształcenia, kod powstaje w parze z asystentem AI, a projekt, decyzje i testy są moje. Te notatki opisują, co się w tych systemach zepsuło i co z tego wyszło.

Masz pytanie do tego tekstu?

Napisz do mnie