Files
rustpad/SECURITY_CHANGES.md
T

76 lines
4.6 KiB
Markdown

# RustPad — analiza i lista poprawek
## Zrealizowane zmiany
### 1. Kontrola `Origin` dla WebSocketów
- Żądanie upgrade jest odrzucane kodem `403`, gdy brakuje nagłówka `Origin`, ma on wartość `null`, jest nieprawidłowy albo wskazuje inną domenę.
- Porównywane są schemat i authority z rzeczywistym nagłówkiem `Host`.
- `X-Forwarded-Host` nie jest używany do autoryzacji, aby nie dopuścić do obejścia przez sfałszowany nagłówek.
- Gdy reverse proxy przekazuje `X-Forwarded-Proto`, schemat `Origin` musi być z nim zgodny.
- Kontrola obejmuje WebSockety workspace i pojedynczej notatki.
### 2. Tokeny konta poza `localStorage`
- Token konta jest wydawany wyłącznie jako ciasteczko `__Host-rustpad_session` z flagami:
- `HttpOnly`
- `Secure`
- `SameSite=Lax`
- `Path=/`
- Token konta nie jest zwracany w JSON, wysyłany w wiadomości WebSocket ani przyjmowany jako token konta z `Authorization`.
- Frontend usuwa stare wartości `rustpad:auth-token` i `rustpad:access:*` zawierające sekrety.
- `localStorage` zawiera jedynie niesekretne znaczniki stanu.
- Token dostępu po poprawnym haśle zasobu również trafia do ciasteczka `HttpOnly; Secure; SameSite=Lax`.
- Hasło strony publicznej nie jest już zapisywane w `sessionStorage`.
### 3. Limitowanie prób
Wprowadzono odpowiedzi `429 Too Many Requests` dla:
| Operacja | Limit szczegółowy | Limit klienta | Okno |
|---|---:|---:|---:|
| Logowanie | 5 prób na login | 30 prób | 15 min |
| Żądanie resetu hasła | 3 próby na e-mail | 10 prób | 60 min |
| Potwierdzenie resetu | 10 prób na token | 20 prób | 15 min |
| Hasło workspace/notatki | 10 prób na zasób | 50 prób | 15 min |
- Limity haseł zasobów działają w endpointach HTTP, endpointach wydających token dostępu oraz w obu kanałach WebSocket.
- Poprawna próba zeruje wyłącznie licznik konkretnego loginu, tokenu lub zasobu; nie zeruje limitu globalnego klienta.
- Klucz klienta korzysta z poprawnego adresu IP przekazanego przez zaufane proxy, z bezpiecznym fallbackiem, gdy IP nie jest dostępne.
### 4. Bezpieczne serwowanie plików
- Obrazy rastrowe PNG, JPEG, GIF, WebP, AVIF, BMP i ICO nadal mogą być wyświetlane inline.
- HTML, SVG, XML, PDF oraz wszystkie pozostałe typy są zwracane jako `application/octet-stream` z `Content-Disposition: attachment`.
- Dodano `X-Content-Type-Options: nosniff` oraz restrykcyjny `Content-Security-Policy`.
- Nazwa pliku w `Content-Disposition` jest sanityzowana.
### 5. Upload tylko dla zalogowanych kont
- Serwer wymaga ważnej sesji konta przed przyjęciem multipart uploadu do workspace lub notatki.
- Gość korzystający z publicznego zasobu, hasła zasobu albo linku udostępnienia nie może wgrywać plików.
- Frontend wcześniej blokuje przycisk i wyświetla komunikat, ale kontrola serwerowa pozostaje źródłem prawdy.
### 6. Widok mobilny
- Opcje edytora mobilnego używają tego samego stylu checkboxów co konfiguracja strony.
- Usunięto zduplikowane i nieużywane reguły CSS pozostałe po poprzedniej implementacji.
## Kontrola wykonania
- Sprawdzenie składni wszystkich plików JavaScript: zaliczone.
- Sprawdzenie pozostałości sekretów zapisywanych przez frontend: zaliczone.
- Sprawdzenie obecności zabezpieczeń cookies, WebSocketów, uploadu, plików i odpowiedzi `429`: zaliczone.
- Sprawdzenie nawiasów i zgodności zmienionych wywołań Rust: zaliczone statycznie.
- Sprawdzenie końcowego diffu pod kątem białych znaków: zaliczone.
- Kompilacja, `cargo fmt`, `cargo check` i testy Rust nie zostały uruchomione, ponieważ środowisko nie zawiera toolchainu Rust.
## Lista przed wdrożeniem
1. Wdrożyć wyłącznie przez HTTPS — ciasteczka z flagą `Secure` nie będą działać przez zwykły HTTP.
2. Reverse proxy musi zachowywać prawidłowy `Host` oraz nadpisywać, a nie ufać nagłówkom klienta: `CF-Connecting-IP`, `X-Real-IP`, `X-Forwarded-For` i `X-Forwarded-Proto`.
3. Przy wdrożeniu unieważnić istniejące sesje kont i istniejące tokeny dostępu do zasobów. Sekret wcześniej zapisany w przeglądarce mógł zostać skopiowany przed aktualizacją.
4. Dla wielu instancji aplikacji przenieść limiter do współdzielonego magazynu, np. Redis. Obecny limiter działa w pamięci pojedynczego procesu i zeruje się po restarcie.
5. W CI uruchomić co najmniej: `cargo fmt --check`, `cargo check`, `cargo test` oraz testy integracyjne logowania, uploadu, plików i WebSocketów za docelowym reverse proxy.
6. Zweryfikować ustawienia domen i subdomen. Jeżeli niezaufana treść może działać na subdomenie tej samej witryny, warto dodatkowo wdrożyć tokeny CSRF dla operacji modyfikujących dane.