Bezpieczne przesyłanie plików w PHP nie polega na zaakceptowaniu formularza i przeniesieniu pliku na serwer. Załącznik może stać się wektorem wykonania, wyciekiem informacji, przesłaniem wyczerpującym zasoby lub dokumentem niedostępnym, gdy proces biznesowy go potrzebuje. Projekt musi obejmować pełny cykl: odbiór, walidację, przechowywanie, przetwarzanie, autoryzację dostępu, audyt i usuwanie.
Zdefiniuj kontrakt biznesowy przed zaakceptowaniem plików

Pierwsza kontrola nie jest techniczna: należy ograniczyć potrzebę, którą rozwiązuje każdy załącznik. Dokument tożsamości, faktura i zdjęcie profilowe mają różne formaty, właścicieli, okresy retencji oraz uprawnienia do wglądu. Grupowanie ich pod ogólną opcją „prześlij plik” utrudnia stosowanie odpowiednich kontroli.
Dla każdego typu dokumentu zdefiniuj jawny kontrakt:
- Jakie formaty są potrzebne, a jakie wykluczone.
- Maksymalny rozmiar, maksymalna liczba załączników na operację oraz skumulowany limit dla konta lub sprawy.
- Kto może go przesłać, na jakim etapie procesu oraz czy może go zastąpić lub usunąć.
- Jakiej automatycznej walidacji i jakiego przeglądu przez człowieka wymaga.
- Kto może go wyświetlić, pobrać lub zażądać nowej wersji.
- Jak długo jest przechowywany i jakie zdarzenie uruchamia jego usunięcie.
Ten kontrakt zapobiega akceptowaniu plików „na wszelki wypadek”. Pozwala też odróżnić błąd walidacji, który użytkownik może poprawić, od ograniczenia biznesowego, takiego jak próba dołączenia dokumentu, gdy sprawa jest już zamknięta.
Dlaczego rozszerzenie i zadeklarowany typ nie wystarczą
Rozszerzenie oryginalnej nazwy i typ przesłany w $_FILES['type'] to dane dostarczone przez klienta. Służą jako pomocnicza informacja dla interfejsu, ale nie potwierdzają zawartości. Zmiana nazwy pliku jest trywialna, a klient może wysłać dowolny nagłówek HTTP.
Walidacja techniczna powinna stosować obronę warstwową. W PHP wykrywaj typ na podstawie otrzymanych bajtów za pomocą mechanizmów takich jak finfo; następnie stosuj walidatory właściwe dla danego formatu, gdy wymaga tego ryzyko lub sposób użycia. W przypadku obrazu, który ma być wyświetlany, nie wystarczy zidentyfikować go jako obraz: warto zdekodować go przy użyciu odpowiedniej biblioteki i wygenerować nową reprezentację. W przypadku pliku PDF lub dokumentu biurowego waliduj oczekiwaną strukturę za pomocą wyspecjalizowanych narzędzi w odizolowanym środowisku.
Lista dozwolonych elementów jest bezpieczniejsza niż lista zablokowanych. Jeśli przypadek użycia dopuszcza JPEG i PNG, odrzuć pozostałe formaty zamiast próbować wyliczyć niebezpieczne formaty. Pliki skompresowane wymagają dodatkowej kontroli: limit rozmiaru skompresowanego nie ogranicza rozmiaru po rozpakowaniu. Ustal limity rozmiaru po rozpakowaniu, liczby wpisów, głębokości zagnieżdżenia i czasu analizy.
Nazwy również są niezaufane. Nie używaj ich jako ścieżki, identyfikatora ani nazwy fizycznej. Sekwencje takie jak ../, znaki kontrolne, podwójne rozszerzenia i kolizje nazw powinny stracić znaczenie, ponieważ system generuje własny nieprzezroczysty identyfikator.
Kontrolowany odbiór i obsługa częściowych błędów
Przed przetwarzaniem zawartości ogranicz powierzchnię wejściową. Skonfiguruj spójne limity w PHP, serwerze WWW i reverse proxy. Jeśli proxy akceptuje 100 MB, ale PHP dopuszcza 10 MB, zachowanie będzie mylące; jeśli PHP zezwala na więcej, niż przewidziano, atakujący może zużyć pamięć, dysk tymczasowy lub połączenia robocze.
Jawnie kontroluj rozmiar pojedynczego pliku, łączny rozmiar żądania, liczbę plików i czas przesyłania. Sprawdzaj kod błędu każdego wpisu w $_FILES, weryfikuj, czy pochodzi on z przesłania HTTP, i traktuj każdy załącznik jako niezależną jednostkę. W operacji wielokrotnej zdecyduj z góry, czy wynik jest atomowy, czy częściowy. Jeśli dopuszczane są wyniki częściowe, odpowiedź musi dokładnie wskazywać, który plik został odebrany, który odrzucony i dlaczego, bez ujawniania wewnętrznych szczegółów serwera.
Nie przetwarzaj pliku bezpośrednio ze ścieżki kontrolowanej przez żądanie ani nie zakładaj, że przerwane przesyłanie jest nieszkodliwe. Niekompletne pliki tymczasowe należy usuwać, a ponowienia powinny być idempotentne, gdy produkt je obsługuje. Token operacji lub klucz idempotencji zapobiega utworzeniu wielu równoważnych załączników po ponownych wysłaniach sieciowych.
Prywatne przechowywanie, kwarantanna i przetwarzanie
Pliki binarne nie powinny znajdować się w publicznym katalogu aplikacji. Przechowuj je w prywatnym storage, z poświadczeniami minimalnych uprawnień, i powiąż każdy obiekt z wewnętrznym identyfikatorem wygenerowanym przez system. Baza danych może przechowywać metadane takie jak właściciel, kontekst biznesowy, wykryty typ, rozmiar, odcisk kryptograficzny, stan, daty i polityka retencji. Unikaj przechowywania niepotrzebnych danych osobowych w nazwie lub w zapisach operacyjnych.
Solidny przepływ rozdziela odbiór i dostępność:
- Aplikacja odbiera plik i tworzy rekord w stanie
oczekującylubw_kwarantannie. - Plik binarny jest umieszczany w lokalizacji niedostępnej do pobierania.
- Proces asynchroniczny wykonuje skanowanie antimalware, pogłębioną walidację, transformację lub dozwoloną ekstrakcję.
- Wynik zmienia się na
dostępny,odrzuconylubwymaga_weryfikacji. - Interfejs pokazuje stan operacyjny, nie udając, że przesłany plik jest już używalny.
Kolejki skracają czas odpowiedzi, ale wprowadzają własne awarie: zduplikowane zadania, opóźnione komunikaty i procesy przetwarzające, które przestały działać. Projektuj idempotentne zadania, limity ponowień, alerty dla zablokowanych elementów oraz kontrolowaną ścieżkę ponownego przetwarzania. Jeśli analiza nie jest dostępna, bezpieczną alternatywą jest zazwyczaj pozostawienie załącznika w kwarantannie, a nie jego publikowanie.
Autoryzuj każde pobranie i dostarczaj treść bez jej wykonywania
Sama znajomość identyfikatora nie daje dostępu. Każde pobranie musi sprawdzać uwierzytelnioną tożsamość, jej aktualną relację z zasobem i kontekst biznesowy: przynależność do organizacji, przypisanie do sprawy, aktualną rolę, stan dokumentu i ograniczenia czasowe. Nie używaj ponownie autoryzacji obowiązującej podczas przesyłania pliku; uprawnienia mogły zostać później cofnięte.
Pobieranie powinno przechodzić przez kontroler, który stosuje tę decyzję przed odczytaniem lub delegowaniem obiektu. Podpisane URL mogą być przydatne do dostarczania ze storage zewnętrznego, ale wymagają ograniczonego zakresu, krótkiego czasu wygaśnięcia i kontroli zapobiegających ich wystawianiu dla nieautoryzowanych zasobów.
Aktywne typy dostarczaj ostrożnie. W przypadku dokumentów, które nie są przeznaczone do renderowania w przeglądarce, użyj Content-Disposition: attachment. Ustaw typ zawartości na podstawie zwalidowanego typu, a nie rozszerzenia, i dodaj X-Content-Type-Options: nosniff. Podgląd nie jest zwykłym osadzonym pobraniem: powinien używać przekształconych reprezentacji, izolować potencjalnie aktywną treść i unikać ujawniania wewnętrznych ścieżek.
Audyt, odtwarzanie i testy przed produkcją

Rejestruj istotne zdarzenia: utworzenie, zastąpienie, zmianę stanu, pobranie, usunięcie, błąd analizy i modyfikację uprawnień. Rejestr powinien zawierać aktora, moment, zasób i wynik, ale nie musi przechowywać pliku binarnego ani zduplikowanych danych wrażliwych. Chroń te zdarzenia przed zmianami i określ, kto może je przeglądać.
Odtwarzanie wymaga wiedzy, co dzieje się w razie awarii storage, bazy danych lub procesora. Nie oznaczaj dokumentu jako dostępnego, dopóki plik binarny i jego metadane nie są spójne. Projektuj zadania uzgadniające, aby wykrywać rekordy bez obiektu, osierocone obiekty i elementy, które zbyt długo pozostają w kwarantannie.
Lista kontrolna przed uruchomieniem
- Dozwolone formaty odpowiadają udokumentowanemu przypadkowi użycia i są walidowane na podstawie zawartości.
- Istnieją limity rozmiaru, liczby, czasu i ekspansji plików skompresowanych.
- Oryginalne nazwy nie określają ścieżek ani nazw fizycznych.
- Pliki binarne są przechowywane poza katalogiem publicznym i, gdy to właściwe, przechodzą przez kwarantannę.
- Pobieranie ponownie ocenia autoryzację i nie ujawnia wewnętrznych ścieżek.
- Testowane są pliki uszkodzone, duplikaty, przerwane przesyłania, cofnięte uprawnienia i awarie storage.
- Istnieje monitorowanie odrzuceń, zablokowanych zadań, błędów dostarczania i zużytej pojemności.
- Polityki retencji, usuwania i audytu mają właściciela operacyjnego.
Przekształcenie przesyłania w przepływ ze stanami i kontrolami może wydawać się bardziej kosztowne niż użycie katalogu uploadów. Zmniejsza jednak liczbę improwizowanych decyzji, gdy pojawia się podejrzany plik, zgłoszenie dotyczące dostępu lub przerwa operacyjna. Ta identyfikowalność jest częścią produktu, a nie późniejszym dodatkiem.



