Skip to content

feat(browse): cache stron autor/jednostka/zrodlo/uczelnia przez @csrf_exempt (2.1)#650

Merged
mpasternak merged 1 commit into
devfrom
fix/browse-csrf-exempt-cache
Jul 24, 2026
Merged

feat(browse): cache stron autor/jednostka/zrodlo/uczelnia przez @csrf_exempt (2.1)#650
mpasternak merged 1 commit into
devfrom
fix/browse-csrf-exempt-cache

Conversation

@mpasternak

Copy link
Copy Markdown
Member

Problem

PR #633 wykluczył z cache'owania strony browse/autor, jednostka, zrodlo,
uczelnia — bo renderują bezwarunkowy {% csrf_token %} w formularzu „szukaj
publikacji", a strażnik _ZAWIERA_CSRF odmawia cache'owania odpowiedzi z tokenem.
Strona autora ma wysoki ruch (poz. 2.1 audytu).

Zmiana — @csrf_exempt zamiast endpointu tokenowego

BuildSearch (widok przyjmujący formularz) dostaje @csrf_exempt, token usunięty
z formularzy w 4 szablonach, strony dodane do cache_publiczny().

Dlaczego bezpieczne: BuildSearch.post() zapisuje WYŁĄCZNIE do sesji
requestującego (wartość = json.dumps, konsumowana przez json.loads+
recreate_form — bez pickle/eval; tytuł przez nh3.clean), po czym redirect.
Zero uprzywilejowanej akcji, zero mutacji wspólnego stanu. Najgorszy skutek
CSRF-ataku = podsunięcie ofierze wyszukiwania w jej sesji — bez szkody. Ta sama
funkcja jest i tak publiczna (API). Kierunek endpointu tokenowego świadomie
odrzucony. CSRF_COOKIE_HTTPONLY = True NIETKNIĘTE.

Weryfikacja

  • Cache na realnym backendzie (LocMem, nie DummyCache): MISS→HIT + identyczna treść.
  • Separacja hostów (multi-tenant): request.get_host() jawnie w kluczu cache;
    test dowodzi obcy host = MISS, brak wycieku między uczelniami.
  • POST bez tokenu = 302, nie 403 (klient z enforce_csrf_checks=True).
  • Review PASS + APPROVED (wektory bezpieczeństwa prześwietlone: sesja/deserializacja/
    open-redirect/XSS). Token zdjęty z dokładnie 8 właściwych formularzy, żaden inny
    stanowy formularz nie rozbrojony. Strażnik _ZAWIERA_CSRF zostawiony jako
    defense-in-depth.

Uwaga: zmiana dotyka @csrf_exempt na publicznym widoku — zasługuje na
/security-review przed mergem (wektory zweryfikowane już w review, ale to
mechanizm bezpieczeństwa).

Poz. 2.1 audytu.

🤖 Generated with Claude Code

https://claude.ai/code/session_019GmViAsaif9MXeuDMA5NHX

…_exempt

Strony przeglądania autora (wysoki ruch), jednostki, źródła i uczelni
były wykluczone z cache_publiczny, bo renderowały bezwarunkowy
{% csrf_token %} w formularzu „szukaj publikacji", a bezpiecznik
_ZAWIERA_CSRF odmawia zapisu każdej odpowiedzi z „csrf" w treści.

Zamiast endpointu tokenowego: BuildSearch dostaje @csrf_exempt (jego
jedyny efekt uboczny to zapis wyszukiwania do WŁASNEJ sesji
requestującego — brak uprzywilejowanej akcji, patrz docstring), a
{% csrf_token %} znika z 8 formularzy w 4 szablonach browse. Dzięki
temu treść nie niesie już sekretu i wchodzi do współdzielonego cache'a.

Bezpiecznik _ZAWIERA_CSRF zostaje jako defense-in-depth. Cztery widoki
DetailView dostają @method_decorator(cache_publiczny()).

Testy (LocMemCache, nie DummyCache): 4 strony faktycznie się cache'ują
(MISS→HIT), POST na build_search bez tokenu nie zwraca 403 (302),
oraz izolacja multi-host strony autora (brak wycieku między uczelniami).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019GmViAsaif9MXeuDMA5NHX
@mpasternak
mpasternak merged commit 597c71b into dev Jul 24, 2026
22 checks passed
@mpasternak
mpasternak deleted the fix/browse-csrf-exempt-cache branch July 24, 2026 15:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant