cURL jest programem wiersza poleceń do transferu danych, który wspiera ponad 25 protokołów, w tym najważniejsze jak HTTP, HTTPS, FTP, POP3, SFTP czy IMAP. W moim artykule skupimy się głównie na HTTP/HTTPS, bo jest to dominujący protokół w nowoczesnych systemach SIEM, SaaS czy API, z którego będziemy korzystać zapewne najczęściej.
cURL warto poznać również ze względu na jego popularność. Znajduje się na Linux, macOS czy Windows 10+, wykorzystywany przez administratorów sieci, zespoły Blue Team i Red Team ze względu na pełną kontrolę i przejrzystość.
Pracując z cURL poznamy jak działa HTTP od środka – nagłówki, metody, status codes, cookies, redirecty czy TLS handshake. Jest to wiedza fundamentalna do pracy w IT.
Zaczniemy od podstawowej składni, która w cURL wygląda tak:
curl [opcje] [URL]
Najprostsze wykorzystanie cURL to podanie adresu URL bez żadnych opcji, zwróci on zawartość strony wprost do terminala:
curl https://httpbun.com
Możemy delikatnie zmodyfikować to zapytanie i wykorzystać pierwszą opcję/flagę:
-o <plik> / --output
Przekieruje to odpowiedź do konkretnego pliku:
curl -o strona.html https://httpbun.com
Przejdźmy w takim razie do zapoznania się z najważniejszymi flagami:
Zapisuje plik pod nazwą wziętą z końcówki URL. Dla przykładu https://archiwum.nask.pl/download/30/4940/NASK-raport-rynek-nazw-domeny-pl-2023.pdf zapisze się jako NASK-raport-rynek-nazw-domeny-pl-2023.pdf.
-O / --remote-name
curl -O https://archiwum.nask.pl/download/30/4940/NASK-raport-rynek-nazw-domeny-pl-2023.pdf
Tryb cichy – normalnie w trakcie działania cURL wyświetla pasek postępu i statystyki pobierania. Tryb cichy wyłącza ten szum.
-s / --silent
Poniższa flaga często działa w parze z -s, ponieważ w trybie cichym tracimy również informacje o błędach, co utrudnia debugowanie. -S przywraca informacje o błędach, dlatego w skryptach możemy spotkać flagę -sS.
-S / --show-error
Kolejna flaga pokazuje szczegółowy output. Rozwiązanie DNS, ustanowienie połączenia TCP, TLS handshake, wersję protokołu i wszystkie nagłówki. Jest to flaga kluczowa przy debugowaniu.
-v / --verbose
Bardziej szczegółowa wersja powyższej komendy z zapisaniem do pliku to:
--trace-ascii <plik>
NAGŁÓWKI I METODY HTTP
Podstawowe zapytanie, które wysyła żądanie typu HEAD zamiast GET. Serwer odpowiada wtedy samymi nagłówkami bez treści. Przydatne do sprawdzenia czy serwer odpowiada i co zwraca Content-Type.
-I / --head
Przykład odpowiedzi dla mojego bloga:
HTTP/2 403
date: Tue, 28 Apr 2026 11:50:16 GMT
content-type: text/html; charset=UTF-8
content-length: 5235
accept-ch: Sec-CH-UA-Bitness, Sec-CH-UA-Arch, Sec-CH-UA-Full-Version, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform, Sec-CH-UA, UA-Bitness, UA-Arch, UA-Full-Version, UA-Mobile, UA-Model, UA-Platform-Version, UA-Platform, UA
cf-mitigated: challenge
content-security-policy: default-src 'none'; script-src 'nonce-ptilqWlhT752QmWmX3CTEL' 'unsafe-eval' https://challenges.cloudflare.com; script-src-attr 'none'; style-src 'unsafe-inline'; img-src 'self' https://challenges.cloudflare.com; connect-src 'self' https://challenges.cloudflare.com; frame-src 'self' https://challenges.cloudflare.com blob:; child-src 'self' https://challenges.cloudflare.com blob:; worker-src blob:; form-action http: https:; base-uri 'self'
server: cloudflare
critical-ch: Sec-CH-UA-Bitness, Sec-CH-UA-Arch, Sec-CH-UA-Full-Version, Sec-CH-UA-Mobile, Sec-CH-UA-Model, Sec-CH-UA-Platform-Version, Sec-CH-UA-Full-Version-List, Sec-CH-UA-Platform, Sec-CH-UA, UA-Bitness, UA-Arch, UA-Full-Version, UA-Mobile, UA-Model, UA-Platform-Version, UA-Platform, UA
cross-origin-embedder-policy: require-corp
cross-origin-opener-policy: same-origin
cross-origin-resource-policy: same-origin
origin-agent-cluster: ?1
permissions-policy: accelerometer=(),browsing-topics=(),camera=(),clipboard-read=(),clipboard-write=(),geolocation=(),gyroscope=(),hid=(),interest-cohort=(),magnetometer=(),microphone=(),payment=(),publickey-credentials-get=(),screen-wake-lock=(),serial=(),sync-xhr=(),usb=(),xr-spatial-tracking=(self)
referrer-policy: same-origin
server-timing: chlray;desc="9f35d93379ae0178"
x-content-type-options: nosniff
x-frame-options: SAMEORIGIN
report-to: {"group":"cf-nel","max_age":604800,"endpoints":[{"url":"https://a.nel.cloudflare.com/report/v4?s=zINMwkykEgAurw39U%2FUPH2fJk0E7Co%2FWZB7%2FtRikW02DcCR3FOgMyxc1PCKoSaSzkIcepuyMeIsHyNyaOWF%2FcKGl6zV7nVmWY7D6jBsByrPhAsJdpDelb1kheWpuEJRxpBA%3D"}]}
nel: {"report_to":"cf-nel","success_fraction":0.0,"max_age":604800}
cf-ray: 9f35d93379ae0178-WAW
alt-svc: h3=":443"; ma=86400
Z nagłówków widzimy, że kod odpowiedzi to 403, ale nie dlatego, że strona nie istnieje. Cloudflare wykrył połączenie cURL i zwrócił JS Challenge, co widać po nagłówkach:
cf-mitigated: challenge
server: cloudflare
Poniższa flaga dołącza nagłówki odpowiedzi do outputu. Różnica od -I jest taka, że robi normalne zapytanie GET + treści z -I.
-i / --include
Kolejna flaga pozwala ustawić nagłówek User-Agent. Sam cURL wysyła domyślnie coś w stylu curl/7.88.1, co jest blokowane przez niektóre serwery, jak przekonaliśmy się wcześniej.
-A <string> / --user-agent
Spróbujemy jakiego user-agenta, który będzie podszywać się pod prawdziwą przeglądarkę i sprawdzimy odpowiedź Cloudflare.
Safari na iPhone:
curl -A "Mozilla/5.0 (iPhone; CPU iPhone OS 17_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Mobile/15E148 Safari/604.1" https://echelonwatch.pl
Cloudflare przepuszcza już taki ruch bez problemu. Odpowiedź serwera:
<!DOCTYPE html>
<html lang="pl">
<head>
<title>EchelonWatch - Monitorowanie cyberzagrożeń</title>
<!-- Metadane SEO -->
<!-- Meta podstawowe -->
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="ECHELONWATCH – blog o cyberbezpieczeństwie, OSINT, analizie zagrożeń i technologii.">
Ustawienie Referer – ten nagłówek informuje, skąd przyszedł ruch:
-e <url> / --referer
Wysyłka danych w body i zmiana wtedy z GET na POST oraz automatyczne ustawienie Content-Type na application/x-www-form-urlencoded:
-d <dane> / --data
Wersja z kodowaniem w formacie URL:
--data-urlencode
COOKIES
Wysyłka cookie w żądaniu:
-b <cookie> / --cookie
Zapis cookie otrzymanych od serwera do pliku:
-c <plik> / --cookie-jar
SIEĆ I PRZEKIEROWANIA
Podążanie za przekierowaniami. W przypadku, gdy serwer odpowie kodem 3xx (redirect):
-L / --location
Przydatna komenda przy analizie pętli redirectów – pozwala ograniczyć liczbę przekierowań:
--max-redirs <n>
Możemy również wysłać nasz ruch przez proxy, np. -x http://127.0.0.1:8000 przepuści nasz ruch przez Burpa:
-x <proxy> / --proxy
UWIERZYTELNIANIE
Możliwości autoryzacji w HTTP:
-u <user:pass> / --user
Przykładowy wynik komendy, jaki otrzymamy po poprawnym zalogowaniu:
curl -u admin:secret https://httpbin.org/basic-auth/admin/secret
{
"authenticated": true,
"user": "admin"
}
FLAGA -w CZYLI ZMIENNE DO FORMATOWANEGO OUTPUTU
Jedna z ciekawszych i rzadziej stosowanych opcji cURLa. Po zakończeniu transferu możemy wpisać sformatowany string zawierający metryki z wykonanej operacji. Działa jak printf – podajemy szablon ze zmiennymi w postaci %{nazwa}, a cURL zastępuje je rzeczywistymi wartościami.
Przykłady zmiennych:
%{http_code}– kod odpowiedzi HTTP (200, 404, 500...)%{url_effective}– finalny URL po wszystkich przekierowaniach%{redirect_url}– URL następnego redirectu (jeśli był)%{remote_ip}– adres IP serwera, z którym faktycznie rozmawialiśmy%{remote_port}– port%{ssl_verify_result}– wynik weryfikacji SSL (0 = OK, inne = błąd)%{content_type}– Content-Type odpowiedzi%{num_redirects}– ile razy nastąpiło przekierowanie%{size_download}– ile bajtów pobrano%{size_upload}– ile bajtów wysłano%{speed_download}– średnia prędkość pobierania%{time_namelookup}– czas rozwiązania DNS%{time_connect}– czas nawiązania połączenia TCP%{time_appconnect}– czas TLS handshake%{time_pretransfer}– czas do momentu gotowości do transferu%{time_starttransfer}– czas do pierwszego bajta odpowiedzi (TTFB)%{time_total}– całkowity czas operacji
Przykład:
curl -o /dev/null -s -w "HTTP: %{http_code} | IP: %{remote_ip} | Czas: %{time_total}s\n" https://example.com
Otrzymamy wynik w terminalu:
HTTP: 200 | IP: 172.66.147.243 | Czas: 0.103003s
-o /dev/null wyrzuca body odpowiedzi, -s ukrywa pasek postępu, a -w wypisuje sformatowane dane.
METODY HTTP
Kiedy mamy omówione już najważniejsze flagi, możemy przejść do bardziej praktycznego zastosowania cURL.
Metodę GET już omówiliśmy na samym początku – jest to domyślne zapytanie, więc sprawdźmy działanie metody POST.
Użycie flagi -d automatycznie zmienia metodę na POST, więc flaga -X jest już zbędna, ale dla jasności postanowiłem ją zostawić w tym przykładzie.
curl -X POST https://api.example.com/login -d "user=admin&pass=secret"
PUT
PUT służy do aktualizacji. Składnia pozostaje prawie identyczna jak POST – różni się tylko metoda. Warto pamiętać, że -d domyślnie ustawia Content-Type na application/x-www-form-urlencoded, więc wysyłając JSON do nowoczesnego REST API, należy jawnie nadpisać nagłówek za pomocą -H "Content-Type: application/json", inaczej serwer może go odrzucić lub źle sparsować.
curl -X PUT https://api.example.com/users/123 -H "Content-Type: application/json" -d '{"name":"echelonwatch"}'
DELETE
DELETE nie potrzebuje body, wystarczy URL wskazujący na konkretny zasób:
curl -X DELETE https://api.example.com/users/123
Upload pliku używa formatu multipart/form-data – tego samego, którego używają formularze HTML z <input type="file">. Prefiks @ oznacza „wczytaj z pliku”, a pole description to zwykłe pole tekstowe. Możemy łączyć oba typy w jednym wywołaniu.
curl -F "file=@/path/do/pliku.pdf" -F "description=raport" https://api.example.com/upload
AUTORYZACJA
Basic Auth mamy już za sobą, więc omówmy Bearer Token. Jest to najpopularniejszy mechanizm w nowoczesnych REST API (OAuth 2.0, JWT). Jest to string, który serwer waliduje. Korzystamy z flagi -H:
curl -H "Authorization: Bearer eyJhbGciOi..." https://api.example.com
Kolejną bardzo przydatną opcją jest API Key w nagłówku. Pozwala nam odpytać takie usługi jak VirusTotal czy Shodan:
curl -H "x-apikey: TWOJ_KLUCZ" https://api.example.com
TROCHĘ PRAKTYCZNEGO ZASTOSOWANIA W SOC
Proste sprawdzenie nagłówków serwera bez pobierania treści:
curl -I https://httpbin.org
Odpowiedź:
HTTP/2 200
date: Wed, 29 Apr 2026 18:41:00 GMT
content-type: text/html; charset=utf-8
content-length: 9593
server: gunicorn/19.9.0
access-control-allow-origin: *
access-control-allow-credentials: true
Błyskawicznie otrzymujemy informacje, jaki webserver jest używany, nagłówki bezpieczeństwa, a jeżeli strona jest za Cloudflare, widzimy CF-RAY i Server: cloudflare.
Śledzenie łańcucha przekierowań jest bardzo przydatne, np. przy analizie phishingu:
curl -sIL https://httpbin.org/redirect/3 | grep -iE "^HTTP|^location:"
Odpowiedź serwera – bardzo fajnie widzimy wszystkie „przystanki”:
HTTP/2 302
location: /relative-redirect/2
HTTP/2 302
location: /relative-redirect/1
HTTP/2 302
location: /get
HTTP/2 200
Zastosowanie wspomnianej wcześniej flagi -w.
Body standardowo wyrzucamy za pomocą -o /dev/null i otrzymujemy czasy odpowiedzi dla DNS, TLS itd., co w przypadku anomalii, np. bardzo długiego czasu odpowiedzi, może świadczyć o DNS hijacking, a wysokie TTFB o przeciążeniach serwera.
curl -o /dev/null -s -w \
"DNS: %{time_namelookup}s
TCP: %{time_connect}s
TLS: %{time_appconnect}s
TTFB: %{time_starttransfer}s
Total: %{time_total}s
" https://httpbin.org
Odpowiedź:
DNS: 0.028969s
TCP: 0.155727s
TLS: 0.424003s
TTFB: 0.574892s
Total: 0.575223s
cURL to niezwykle rozbudowane narzędzie, które daje duże możliwości. Jak zwykle nie da się opisać wszystkiego tylko w jednym artykule, ale mam nadzieję, że zachęciłem do korzystania i eksperymentowania z cURL.