REST API opiera się na prostej konwencji — metoda HTTP mówi co robimy z zasobem. GET czyta, POST tworzy, PUT nadpisuje, DELETE usuwa. Problem zaczyna się, gdy atakujący podmieni metodę w locie i zamieni odczyt w usunięcie.

Jak podmienić metodę?

Nagłówek X-HTTP-Method-Override

Wysyłasz POST, ale nagłówek mówi serwerowi, żeby traktował request jako inną metodę:

curl -X POST https://api.example.com/users/42 \
  -H "X-HTTP-Method-Override: DELETE"

Serwer dostaje POST, ale aplikacja interpretuje to jako DELETE. Użytkownik 42 znika.

Warianty nagłówka — różne serwery czytają różne nazwy:

X-Method-Override: PUT
X-HTTP-Method: PATCH

Mechanizm powstał z praktycznych powodów — starsze przeglądarki i firewalle przepuszczały tylko GET i POST. Żeby wysłać DELETE z formularza HTML, trzeba było opakować go w POST z dodatkowym nagłówkiem.

Parametr w query stringu

POST /users/42?_method=DELETE
POST /users/42?method=PUT

Pole w formularzu HTML

<form method="POST" action="/users/42">
  <input type="hidden" name="_method" value="DELETE">
  <button type="submit">Usuń</button>
</form>

Bezpośrednia podmiana — proxy

Burp Suite, mitmproxy, Zap — przechwytują request i pozwalają zmienić metodę przed wysłaniem do serwera:

GET /api/users/42 HTTP/1.1

Zmiana na:

DELETE /api/users/42 HTTP/1.1

Skrypt mitmproxy do automatycznej podmiany:

from mitmproxy import http

def request(flow: http.HTTPFlow):
    if flow.request.path.startswith("/api/users"):
        flow.request.method = "DELETE"

Konsekwencje

Ominięcie WAF

Typowy scenariusz — WAF filtruje requesty po metodzie HTTP:

# Reguła WAF: blokuj DELETE na /api/users/*
if method == "DELETE" and path matches "/api/users/*":
    block()

Atakujący wysyła:

curl -X POST https://api.example.com/api/users/42 \
  -H "X-HTTP-Method-Override: DELETE"

WAF widzi POST — przepuszcza. Aplikacja widzi DELETE — usuwa użytkownika.

Ominięcie CORS

Przeglądarka wysyła preflight (OPTIONS) tylko dla metod PUT, DELETE, PATCH. GET i POST przechodzą bez preflightu przy prostych nagłówkach.

POST z X-HTTP-Method-Override: DELETE — przeglądarka nie wyśle preflightu, bo POST jest “bezpieczny”. Serwer wykona DELETE.

Obejście rate limitingu

Rate limiter liczy requesty per metoda:

GET /api/data — limit 100/min
POST /api/data — limit 10/min

GET z X-HTTP-Method-Override: POST — rate limiter widzi GET (wysoki limit), aplikacja przetwarza POST.

Cache poisoning

Reverse proxy (Varnish, CDN) cache’uje odpowiedzi na GET. Atakujący wysyła:

curl -X GET https://api.example.com/users/42 \
  -H "X-HTTP-Method-Override: DELETE"

Proxy cache’uje to jako GET, backend wykonuje DELETE. Kolejni użytkownicy dostają cache’owaną odpowiedź na request, który usunął zasób.

Zmiana GET w POST

curl -X GET "https://api.example.com/admin/reset-db" \
  -H "X-HTTP-Method-Override: POST"

Jeśli /admin/reset-db reaguje na POST — GET z override wykona tę akcję.

Jak się bronić?

Najprościej — wyłączyć method override. Nowoczesne przeglądarki i klienty HTTP obsługują wszystkie metody, więc obejście przez POST nie jest już potrzebne.

Jeśli override musi zostać — dwie zasady:

  • pozwalaj na override tylko z POST (nigdy z GET — GET nie powinien mieć side effectów)
  • nie polegaj na metodzie HTTP w kontroli dostępu — sprawdzaj uprawnienia per akcja, nie per metoda

Na reverse proxy warto usuwać nagłówki override zanim trafią do backendu:

proxy_set_header X-HTTP-Method-Override "";
proxy_set_header X-Method-Override "";
proxy_set_header X-HTTP-Method "";