# Bezpieczeństwo -- pełny obraz

Ten dokument to jedno miejsce, w którym widać **wszystko**, co chroni SameBeat --
niezależnie od tego, na którym etapie dana rzecz powstała. Jeśli szukasz "czy to
jest już zabezpieczone" -- szukaj tutaj, nie w historii etapów w `ARCHITEKTURA.md`.

> **Stan na 2026-08-21 (po ETAPIE 7):** poniższa tabela pokazuje, co **faktycznie
> działa już w kodzie** -- sprawdzone bezpośrednio w plikach backendu, nie na
> podstawie planów.

## Co jest wdrożone -- pełna tabela

| Zagrożenie | Jak jest zabezpieczone | Od którego etapu |
|---|---|---|
| Sekret (hasło, klucz API) trafia do repozytorium kodu | Pliki z prawdziwymi danymi (`appsettings.Development.json`, `appsettings.Production.json`) mają być wykluczone z wersjonowania; jeśli sekret kiedyś wycieknie, trzeba go unieważnić i wygenerować nowy, nie tylko usunąć z kolejnego commita | Od początku |
| Plik z hasłami produkcyjnymi dostępny publicznie przez Nginx | `appsettings.Production.json` leży w `/etc/samebeat/`, **poza** katalogiem `/opt/samebeat/frontend`, który Nginx wystawia na świat jako pliki statyczne; Nginx nie ma reguły wystawiającej `/etc/samebeat` | Od początku (plan wdrożenia w `VPS.md`) |
| Ktoś bez logowania wchodzi do panelu hosta i rozdaje mikrofon/wyrzuca ludzi | Każda trasa `api/host/...` jest automatycznie chroniona `[Authorize]` (konwencja `KonwencjaAutoryzacjiHosta`) -- token JWT w httpOnly cookie, sprawdzany przy każdym żądaniu. Konto testowe `KontoHostaDev` działa tylko lokalnie, na produkcji nie istnieje | Od początku |
| Podsłuchanie hasła/danych w drodze przez internet | Cały ruch produkcyjny wymuszony na HTTPS (`app.UseHttpsRedirection()` + certyfikat odnawiany automatycznie); bez tego przeglądarki i tak zablokowałyby dostęp do kamery/mikrofonu | Od początku (plan w `VPS.md`) |
| Uczestnik "podbija sobie" uprawnienia w tokenie LiveKit (np. widz zdobywa prawo do nadawania mikrofonu) | Backend sam ustala zakres uprawnień tokenu -- klient nigdy nie decyduje, o co prosi. Widz dostaje token tylko do odbioru. Osoba w poczekalni dostaje token z prawem pokazania **wyłącznie kamery** (LiveKit fizycznie odrzuci próbę wysłania dźwięku) | ETAP 2 |
| Widz podgląda kamerę osoby czekającej w poczekalni, zanim host ją zaakceptuje | Poczekalnia i scena główna to dwa **osobne pokoje LiveKit** -- widz nigdy nie dostaje przepustki do pokoju poczekalni | ETAP 2 |
| Token do sceny głównej (bilet na nadawanie kamery/mikrofonu) wycieka publicznie, bo leciałby przez kanał sterowania do wszystkich | Token do sceny nigdy nie leci przez SignalR. Przeglądarka uczestnika sama, prywatnie, dopytuje backend "czy już mnie wpuszczono" -- używając prywatnego sekretu sesji dostanego tylko sobie przy `POST /api/join`. Zdarzenia SignalR o zmianie sceny niosą tylko numer slotu + nick, nigdy identyfikator uczestnika ani token | ETAP 3 |
| Uczestnik trzyma mikrofon dłużej, niż powinien (np. zmodyfikowana przeglądarka ignoruje polecenie wyłączenia) | Czas mikrofonu liczy **wyłącznie backend**, własnym zegarem, niezależnie od przeglądarki uczestnika (`SerwisTimeraMikrofonu`, działa w tle). Gdy czas minie, backend **sam** woła LiveKit Room Service (`SerwisAdministracjiLiveKit`), żeby fizycznie odebrać prawo do publikacji dźwięku -- to dwa niezależne zabezpieczenia naraz: zapis w bazie (kto powinien mieć głos) + twarde wymuszenie po stronie serwera streamingu | ETAP 5 |
| Ktoś wkleja na czacie kod zamiast tekstu, który wykonałby się u innych widzów (XSS) | Treść wiadomości czatu jest oczyszczana przed zapisem -- znaki `<` i `>` są odrzucane, więc wiadomość zawsze trafia do innych przeglądarek jako zwykły tekst do wyświetlenia, nigdy jako coś do "wykonania" | ETAP 6 |
| Spam na czacie / przeciążenie kanału SignalR (którym leci też sterowanie sceną i mikrofonem) | Backend liczy odstęp od ostatniej wiadomości danej tożsamości czatu (min. 2 sekundy) -- licznik trzymany w bazie, nie w pamięci procesu, więc działa nawet przy wielu instancjach backendu. Host dodatkowo może wyciszyć lub zbanować pojedynczą osobę na czacie | ETAP 6 |
| Wymuszenie kamery/logowania na widza, który chce tylko napisać na czacie | Czat ma własną, lekką tożsamość (sam nick, bez `/join` i bez kamery) -- osobną od tożsamości sceny/poczekalni. Pisać może każdy widz, bez akceptacji hosta | ETAP 6 |
| Ktoś dołącza do grupy sterowania panelu hosta w SignalR bez logowania | Metoda `DolaczDoGrupyHosta()` w `StanImprezyHub.cs` ma `[Authorize]` -- korzysta z tego samego ciasteczka JWT co REST API. Niezalogowany klient dostaje odrzucenie wywołania, nie trafia do grupy "host" | ETAP 7 |
| Uczestnik usunięty ze sceny nadal fizycznie nadaje kamerę (bo jego przeglądarka po prostu ignoruje polecenie) | `DELETE /api/host/stage/slots/{numer}` -- oprócz zmiany statusu w bazie -- woła LiveKit Room Service (`SerwisAdministracjiLiveKit.OdbierzUprawnieniaScenyAsync`), żeby fizycznie odebrać prawo publikacji. To samo dzieje się przy zmniejszeniu układu sceny (osierocone sloty) | ETAP 7 |
| Ktoś zalewa API żądaniami (odczytowymi albo próbami zgadnięcia hasła) | Ogólny limit ~60 żądań/minutę na adres IP dla całego `/api/*`, oraz osobny, surowszy limit (5 prób / 5 minut) na `POST /api/auth/login` | ETAP 7 |
| Próba złamania hasła hosta metodą brute-force (zgadywanie wielu haseł pod rząd) | Po 5 nieudanych logowaniach na to samo konto backend blokuje logowanie na 15 minut, niezależnie od adresu IP atakującego (to druga, niezależna warstwa ochrony obok limitu z wiersza wyżej) | ETAP 7 |
| Wstrzyknięcie złośliwego skryptu, który wykonałby się w przeglądarce (XSS) na poziomie całej strony, nie tylko treści czatu | Nagłówek `Content-Security-Policy` (bez `unsafe-inline` dla skryptów) na każdej odpowiedzi backendu, plus `X-Content-Type-Options`, `X-Frame-Options`, `Referrer-Policy`. **Ważne zastrzeżenie:** to chroni odpowiedzi API -- sama strona SPA (pliki frontendu) będzie serwowana przez Nginx jako pliki statyczne, więc docelowo Nginx musi doklejać ten sam nagłówek do tych plików (opisane w `VPS.md`); do czasu wdrożenia na VPS dodatkową, słabszą warstwą jest znacznik CSP w `frontend/index.html` | ETAP 7 |
| Nadmiernie duże żądanie (celowo albo przez pomyłkę) obciąża serwer | Maksymalny rozmiar treści żądania ograniczony w Kestrel | ETAP 7 |

## Powiązane dokumenty

- `ARCHITEKTURA.md` -- ogólny opis systemu i historia etapów.
- `INSTALACJA.md` -- lokalna instalacja deweloperska.
- `VPS.md` -- plan wdrożenia na serwer produkcyjny.
