Zaawansowany hardening PrestaShop dla sklepów, których nie możesz jeszcze zaktualizować
W naszym forensic walkthrough ataku w stylu Magecart postawiliśmy tezę, która z perspektywy czasu zasługiwała na osobny artykuł: wyczyszczenie malware ze sklepu to nie to samo co naprawienie sklepu. Usunęliśmy skimmer, web shells i SUID-root binaries z tamtego sklepu PrestaShop 1.7.x — a krótko później został trafiony ponownie. Nie przez backdoor, który przeoczyliśmy, ale przez front door: podatność na poziomie aplikacji, którą cleanup ograniczył, ale nigdy nie zamknął. Ten wpis to playbook, który powinniśmy byli zastosować za pierwszym razem — jak utwardzić sklep PrestaShop, którego nie możesz zaktualizować jeszcze, tak żeby usunięcie payloadu zamknęło też dziurę, przez którą przyszedł.
Jeśli możesz dziś zaktualizować do PrestaShop 8 albo 9, zrób to zamiast tego — to jedyna trwała naprawa, a wszystko poniżej jest stopgap. Ale dla realnego sklepu upgrade to projekt wielotygodniowy: kompatybilność modułów, refaktoring theme, przepięcie płatności. Okres przejściowy jest prawdziwy i w jego trakcie jesteś live target. To zaawansowany, incident-grade companion dla dwóch podstawowych przewodników — zacznij od PrestaShop Security Hardening Checklist dla pełnego baseline albo od plain-English guide for store owners, jeśli nie jesteś techniczny — potem wróć tutaj po warstwę, która utrzymuje przy życiu sklep, którego jeszcze nie da się zaktualizować.
1. Znajdź wektor wejścia, nie tylko payload
Najdroższym błędem w incident response jest traktowanie widocznego objawu jak rany. Skimmer w theme JavaScript jest objawem. Raną jest to, co pozwoliło atakującemu zapisać ten JavaScript na dysku — web shell, niezałatany moduł, endpoint injection. Usuń skimmer i zostaw ranę otwartą, a kupiłeś sobie dni, nie bezpieczeństwo.
Zanim cokolwiek utwardzisz, odpowiedz dowodami na jedno pytanie: jak kod został zapisany na dysku? Czytaj access logi wokół timestampów utworzenia plików malware — używaj czasu zmiany inode (ctime), nie czasu modyfikacji (mtime), bo atakujący rutynowo cofają mtime, żeby się ukryć. Skoreluj podejrzane requesty POST, nietypowe parametry query i anomalie rozmiaru odpowiedzi z momentem pojawienia się payloadu. Jeśli nie umiesz nazwać wektora wejścia, zakładaj, że nadal jest otwarty i traktuj każdy krok poniżej jako load-bearing.
2. Virtual-patch podatności, które faktycznie wskazują logi i advisories
Sklepy PrestaShop 1.7.x były trafiane przez szereg weaponised vulnerabilities, których szukają automatyczne skanery. Jeśli nie możesz zaktualizować, patchujesz konkretne ścieżki kodu bezpośrednio — „virtual patch” zastosowany do własnych plików. To stopgaps, więc śledź upstream advisories, ale zamykają drzwi, w które ktoś aktywnie kopie. Zacznij od dowodów: ustal dokładny podatny moduł albo wersję core wskazaną przez logi i właściwe advisories, i patchuj to. Znany attack chain z 2022 łączył dowolne exploitable SQL injection ze Smarty MySQL-cache execution, żeby zamienić dostęp do bazy w uruchamianie PHP — więc wzorzec do obserwowania to injection sięgające bazy sparowane z cache flaw wykonującym kod. Przykład faceted-search poniżej jest jednym z takich injection vectors, ale patchuj zainstalowaną wersję ps_facetedsearch tylko wtedy, gdy potwierdzisz, że jest podatna; połowa SQL-injection tego łańcucha równie dobrze może pochodzić z innego kodu core albo modułu.
2.1 SQL injection w faceted-search (jeśli zainstalowana wersja jest podatna)
Niektóre starsze buildy modułu ps_facetedsearch konkatenowały wartości filtrów prosto do klauzuli SQL WHERE — blind SQL-injection primitive. Potwierdź względem advisories, że wersja, którą uruchamiasz, faktycznie jest dotknięta, zanim ją ruszysz; nie każdy sklep 1.7.x ma dokładnie tę lukę. Tam, gdzie ma zastosowanie, naprawą jest przepuszczenie każdej wartości pochodzącej z requestu przez własne escaping PrestaShop, zanim trafi do query: string values opakowane w pSQL() i cudzysłowy, ranges liczbowe twardo rzutowane przez (float). Podatny wzorzec dopisuje current($values) surowo do warunku; spatchowany wzorzec dopisuje pSQL(current($values)) w cudzysłowach dla stringów i wartość po cast dla liczb. Jeśli ta wersja jest potwierdzona jako podatna, to jest zamek na front door. (Ten sam moduł pod obciążeniem crawlerów jest też ryzykiem denial-of-service; osobno opisujemy ten kąt ?q= flood.)
2.2 Smarty MySQL-cache RCE (CVE-2022-36408)
To połowa łańcucha, która zamienia dostęp do bazy w code execution: database-backed cache szablonów Smarty może zostać zmuszony do renderowania PHP kontrolowanego przez atakującego. Większość sklepów nigdy go nie używa. Wyłącz go wprost w config/smarty.config.inc.php, neutralizując warunek, który ładuje SmartyCacheResourceMysql.php i ustawia caching type na mysql — opakuj ten include w permanentnie fałszywy warunek, żeby nigdy nie mógł się wykonać. Potem potwierdź, że sklep faktycznie działa na filesystem caching (ustawienie PS_SMARTY_CACHING_TYPE powinno mieć wartość filesystem, znajdziesz je pod Zaawansowane → Wydajność). SQL injection samo w sobie jest poważne; SQL injection, które może zapisać szablon PHP do cache i go wykonać, to pełne przejęcie. Zamknij obie połowy i łańcuch jest przerwany.
2.3 Porzucone moduły i pliki, do których atakujący wracają
Zrób audyt mechanicznie. Wypisz stare moduły, wykonywalne pliki w zapisywalnych ścieżkach mediów i niedawno zmienione PHP/JS, zanim uznasz cleanup za skończony:
find modules -maxdepth 2 -type f -name '*.php' -mtime +730 -print
find img upload download -type f -name '*.php' -print
find . -type f \( -name '*.php' -o -name '*.js' \) -mtime -14 -print
Stare, nieutrzymywane moduły (rodzina RevSlider to klasyczny przykład) są powtarzalnym wektorem. Trwała naprawa to odinstalowanie i zastąpienie modułu — rób to wszędzie, gdzie możesz. Jeśli naprawdę nie możesz go usunąć, bo theme od niego zależy, nie dawaj blanket-deny na bezpośrednie PHP w całym folderze modułu: wiele modułów legalnie wystawia direct entry points — AJAX scripts, cron URLs, payment callbacks, legacy validation scripts — a Require all denied na każde .php je zepsuje. Zamiast tego najpierw zaudytuj trasy i endpointy modułu, potem blokuj tylko konkretne znane porzucone entry points, do których atakujący wracają, dodając scoped rule .htaccess (Require all denied) dla tych pojedynczych plików. Następnie zablokuj statyczne punkty wejścia, do których atakujący wraca po każdym cleanup — na filesystemach obsługujących extended attributes (ext4, xfs, btrfs) ustaw index.php i pliki config jako immutable przez chattr +i. Przyszły shell nie nadpisze immutable file bez wcześniejszego chattr -i, co wymaga roota, którego nie powinien mieć. Pełny zestaw reguł dla root directory i admin folder, na którym to bazuje, znajdziesz w naszym .htaccess security and performance rules.
3. Zablokuj runtime PHP — poprawnie
Wyłączenie funkcji PHP, których shell używa do uruchamiania procesów systemowych, ma sens, ale to krok najczęściej robiony źle, w sposób wyglądający na zastosowany, lecz niedziałający. Blocklist, której chcesz, obejmuje rodzinę process-spawning: exec, system, shell_exec, passthru, popen, proc_open i pokrewne, plus dl, show_source oraz phpinfo. Samo ustawienie jest łatwe. Trzy rzeczy, które ludzie pomijają, decydują, czy to cokolwiek robi:
- disable_functions jest PHP_INI_SYSTEM. Jest egzekwowane tylko ze startowego php.ini dokładnie tego SAPI, które serwuje stronę. Na panel-managed box (Plesk, cPanel, CloudPanel) web SAPI to zwykle konkretny PHP-FPM pool albo per-version Apache instance — nie systemowy CLI PHP, do którego najpierw trafiasz z shella. Edytuj właściwy php.ini i zrestartuj dokładnie tę usługę, albo zmiana nic nie robi, choć wygląda na gotową.
- Sprawdzaj disable_functions z właściwego SAPI. ini_get('disable_functions') uruchomione z CLI może nie pasować do tego, co faktycznie egzekwuje web SAPI — CLI PHP i web PHP-FPM pool mogą mieć różne konfiguracje. Jedyny wiarygodny test jest realny, wykonany przez web: tymczasowy skrypt o losowej nazwie, usunięty natychmiast, który wywołuje function_exists('exec') i próbuje faktycznego disabled call, np. @exec('id'), pobrany przez HTTP. Ufaj wynikowi próby, nie wartości konfiguracji odczytanej ze złego SAPI.
- To nie jest kill switch. Zatrzymuje PHP shell przed wychodzeniem do OS. Nie neutralizuje PHP eval shell, który nadal może czytać i pisać pliki wewnątrz open_basedir oraz rozmawiać z bazą credentialami leżącymi w app/config/parameters.php. Traktuj to jako jedną warstwę, nie odpowiedź — dlatego detekcja (krok 5) i WAF (krok 4) stoją obok, nie zamiast tego.
Jedna uwaga kompatybilności, żeby nie zepsuć uploadów: core PrestaShop dotyka exec() w kilku ścieżkach obrazu i MIME-detection, ale mają one fallbacki finfo, więc wyłączenie nie psuje uploadu zdjęć produktów. Mimo to przetestuj upload obrazów na staging przed zastosowaniem na live.
4. Postaw Cloudflare z przodu — jako warstwę virtual-patch
Reverse-proxy WAF to najbliższa rzecz do virtual patch, którą możesz wdrożyć w minuty: sprawdza requesty, zanim dotrą do podatnego kodu. Ale zasługuje na tę rolę tylko wtedy, gdy atakujący nie mogą go ominąć.
4.1 Zamknij origin do Cloudflare
WAF przed origin, który nadal odpowiada na publicznym IP, jest opcjonalny z perspektywy atakującego — po prostu uderza w IP bezpośrednio i pomija proxy. Ogranicz serwer tak, żeby tylko Cloudflare mógł dochodzić do portów 80 i 443: chain iptables akceptujący opublikowane zakresy IPv4 i IPv6 Cloudflare (oraz localhost) na tych portach i dropujący całą resztę. Haczyk, który większość osób pomija: DNS poczty zdradza origin IP. Rekord MX, wpis SPF albo grey-clouded subdomena mail. daje atakującemu prawdziwy adres — a origin firewall sprawia, że ten wyciek jest nieszkodliwy dla web. Proxy każdy web record przez Cloudflare (orange cloud) i mimo wszystko zablokuj origin firewallem. SSH, mail i porty panelu zostaw na własnych regułach dostępu; web lock-down nigdy nie powinien ich dotykać.
4.2 WAF custom rules — i pułapka rozszerzeń
Na planie Cloudflare Free dostajesz pięć custom rules i brak regular expressions — tylko contains, eq, starts_with, ends_with oraz lower(). To wystarczy na skupiony zestaw reguł:
- Blokuj web shells i executable uploads: znane nazwy plików shell, bezpośrednie wywołania PHP porzuconych modułów i każde wykonanie PHP pod katalogami assetów (/img/, /upload/, /themes/, /js/, /cache/), gdzie PHP nigdy nie powinno działać.
- Blokuj injection payloads: stringi path-traversal, requesty po /etc/passwd, union select, information_schema, dosłowny tag otwarcia PHP, base64_decode i ich częste warianty zakodowane.
- Blokuj pliki wrażliwe: /.git/, /.env, composer.json i pliki config — nigdy nie serwuj ich publicznie.
- Blokuj najgorsze nuisance crawlers po user-agent. Jeśli bad-bot traffic jest stałym problemem, a nie jednorazowym incydentem, nasz przewodnik po blocking bad bots and unwanted traffic idzie głębiej niż pięć reguł.
Pułapka, która ugryzła nas: Apache często wykonuje więcej niż .php. Typowa linia AddType/AddHandler mapuje .php3 .php4 .phtml do handlera PHP razem z .php. Reguła blokująca .phtml i .phar, ale zapominająca o .php3 i .php4, zostawia szeroko otwarte drzwi uploadu shella — dokładnie tę lukę znaleźliśmy we własnym zestawie reguł podczas review. Blokuj każde rozszerzenie, które serwer faktycznie wykona, i sprawdź realną konfigurację AddType/AddHandler, zamiast zakładać default.
4.3 Rate-limiting: wiedz, co Twój plan może, a czego nie
Rate-limiting to właściwe narzędzie przeciw crawler floods bijącym w faceted search. Ale bądź precyzyjny co do tier, bo plan Free jest dużo bardziej ograniczony, niż dokumentacja sugeruje na pierwszy rzut oka. Tabela poniżej pokazuje różnicę, która decyduje, czy obrona faktycznie odpali:
| Możliwość | Cloudflare Free | Pro (Advanced Rate Limiting) |
|---|---|---|
| Dopasowanie po URL path | Tak | Tak |
| Dopasowanie po query string (np. ?q= facets) | Nie | Tak |
| Dopasowanie po user-agent / source IP | Nie | Tak |
| Akcja | Tylko Block | Managed challenge, block, log |
| Counting window | Stałe | Konfigurowalne |
Praktyczna konsekwencja: na Free nie możesz zbudować reguły „throttle bots on ?q= faceted URLs”, bo query string jest dla niej niewidoczny. Najlepsze, co daje Free, to tępy per-IP flood limit na ścieżkach non-static. Prawdziwy rate-limiting świadomy query i user-agent, z managed challenge zamiast hard block, który odbiłby też realnych klientów, wymaga planu Pro. Dla aktywnie atakowanego sklepu ekspozycja faceted-search zwykle uzasadnia ten upgrade — porównaj to z kosztem DoS, któremu zapobiega.
5. Dodaj detekcję — żeby runda druga została złapana w godziny, nie dni
Hardening zmniejsza prawdopodobieństwo; nie czyni Cię odpornym. Różnica między złym popołudniem a incydentem klasy disclosure to jak szybko zauważysz. Sklep z naszego case study skimmował dane kart klientów przez prawie dwa dni, zanim ktoś spojrzał. Ustaw obserwację:
- File-integrity monitoring (AIDE, Wazuh, OSSEC albo nawet prosty hashed-manifest cron) nad webroot, alertujący o każdym nowym albo zmienionym pliku PHP, który nie pochodzi z deploya. Nowe PHP poza releasem to najwyższy sygnał alarmowy, jaki możesz podpiąć.
- Scheduled indicator sweeps dla wzorców z realnych ataków — wskaźniki z naszej anatomy of a Magecart attack są gotową listą startową — plus każdy nowy executable-extension file pojawiający się w katalogach assetów albo upload.
- Regularny review Cloudflare Security Events, nie tylko po incydencie. WAF jest też sensorem i loguje próby zablokowane przez reguły, co mówi, czego ktoś próbuje.
6. Panel admina nadal jest drzwiami — zamknij je też

Hardening na poziomie aplikacji jest mocniejszy, gdy powracający sprawcy są widoczni i możliwi do zablokowania.
Wszystko powyżej broni publicznego front end. Nie zostawiaj back office bez hardeningu, zakładając, że atakujący przychodzą tylko przez katalog: wyciekły albo brute-forced login admina omija virtual patches całkowicie. W sklepie, którego nie da się zaktualizować, ma to większe znaczenie, nie mniejsze, bo sam formularz logowania może działać na starym kodzie. Minimum to zmiana nazwy katalogu admina, wymuszenie HTTPS wszędzie (nasz SSL and HTTPS setup guide pokazuje, jak zrobić to czysto) oraz dodanie drugiego czynnika i realnej polityki haseł — pełne omówienie jest w two-factor auth, password policies and admin security. Połącz to z blokowaniem IP powracających sprawców, co przechodzimy w IP bans for problem visitors.
7. Leczeniem nadal jest migracja
Wszystko powyżej to defense-in-depth na oprogramowaniu, które nie dostaje już aktualizacji bezpieczeństwa. Podnosi koszt ataku, zamyka drzwi aktywnie wykorzystywane i kupuje tygodnie albo miesiące potrzebne na realną migrację — ale kupuje czas, nie bezpieczeństwo. Sklep z naszego case study jest dokładnie powodem: został wyczyszczony, a ten sam operator wszedł ponownie przez lukę aplikacji, której cleanup nigdy nie załatał. Zaplanuj przejście na PrestaShop 8 albo 9, na czystej infrastrukturze, z obróconymi credentialami, jako właściwą remediation. Hardening tutaj traktuj jako to, co utrzymuje światło włączone, dopóki tam nie dojdziesz.
A jeśli czytasz to po naruszeniu, a nie przed nim, zmień tor: nasz data breach response guide opisuje, co zrobić w pierwszych godzinach, a anatomy of the attack, która motywowała każdy krok powyżej, pokazuje, z czym naprawdę masz do czynienia.
Komentarze
Brak komentarzy. Bądź pierwszy!
Bądź pierwszy: zadaj pytanie albo podziel się przydatną opinią.
Dodaj komentarz
Dodaj pytanie, szczegół montażu albo opinię, która może pomóc innemu czytelnikowi.