Produkt jest sprzedawany specjalistom ds. bezpieczeństwa, którzy będą go dokładnie analizować przed zaufaniem mu z danymi compliance swojej organizacji. Trzy zasady kierują każdą decyzją projektową:
1. Awaria zamknięta (fail closed). Jeżeli mechanizm bezpieczeństwa nie może działać — Vault niedostępny, łańcuch audytu uszkodzony, dostawca kopii zapasowych niedostępny — system odmawia obsługi tej powierzchni i ujawnia awarię wprost. Nie degraduje się po cichu.
2. Baza danych jest granicą bezpieczeństwa. Izolacja dzierżawców jest egzekwowana przez Row-Level Security PostgreSQL na poziomie bazy danych, nie przez filtry warstwy aplikacji. Błąd aplikacji, który zapomni filtrować po dzierżawcy, jest nieszkodliwy: baza danych zwraca zero wierszy.
3. Wykrywalność manipulacji zamiast jej zapobiegania. Architektura sprawia, że każda manipulacja jest wykrywalna i identyfikowalna przez łańcuch skrótów, znaczniki kotwic i endpoint weryfikacji.
Każda tabela powiązana z dzierżawcą ma następujące ustawienia stosowane w czasie migracji:
ALTER TABLE <tabela> ENABLE ROW LEVEL SECURITY;
ALTER TABLE <tabela> FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON <tabela>
USING (tenant_id::text = current_setting('app.current_tenant_id', true));
FORCE ROW LEVEL SECURITY stosuje politykę nawet do właściciela tabeli. Para GUC (app.current_tenant_id, app.current_org_unit_id) jest ustawiana w bloku SET LOCAL na początku transakcji każdego żądania:
# apps/tenants/context.py
with connection.cursor() as cur:
cur.execute("SET LOCAL ROLE app")
cur.execute("SET LOCAL app.current_tenant_id = %s", [str(tenant.id)])
cur.execute("SET LOCAL app.current_org_unit_id = %s", [str(org_unit.id)])
SET LOCAL oznacza, że wartości GUC wygasą z końcem transakcji. pgbouncer działa w trybie puli transakcyjnej — żadna wartość GUC nie wycieka do kolejnego żądania.
| Rola | Uprawnienia | Używana przez |
|---|---|---|
migrator | DDL; właściciel tabel; BYPASSRLS | Wyłącznie migracje Django — nigdy w czasie działania |
app | SELECT, INSERT, UPDATE, DELETE; ograniczona przez RLS | Środowisko uruchomieniowe aplikacji (ORM Django, widoki DRF) |
audit_writer | Wyłącznie INSERT na audit_log; brak UPDATE i DELETE | Serwis audytowy (wyłącznie zapisy) |
superadmin | BYPASSRLS; SELECT na wszystkich tabelach | Panel super-admina — każde działanie rejestrowane w audycie |
Druga oś izolacji — org_unit_id — ogranicza dostęp dla ról powiązanych z placówkami. ITSO Szkoły A widzi wyłącznie Zadania Kontrolne, incydenty i rekordy samoidentyfikacji Szkoły A. Egzekwowane przez RLS na poziomie (tenant_id, org_unit_id).
Passkeys WebAuthn (FIDO2) są dostępne na wszystkich kontach. Obsługiwane uwierzytelniające: platformy (Touch ID, Windows Hello, biometria Android) i sprzętowe klucze FIDO2 (YubiKey). Walidacja licznika logowań przy każdej asercji; spadł licznika wyzwala automatyczne unieważnienie i alert.
Konfiguracja per Dzierżawca. Obsługiwane przepływy: wyłącznie Authorization Code z PKCE. Walidacja tokenu: podpis (JWKS), iss, aud, exp, nbf, nonce. Preset „Zaloguj się przez Microsoft” (Entra ID / Azure AD).
Konfiguracja per Dzierżawca przez przesłanie metadanych XML IdP. SSO inicjowane przez SP; SSO inicjowane przez IdP domyślnie wyłączone. Mapowanie atrybutów konfigurowalne (e-mail, imię i nazwisko, rola). Provisioning JIT z mapowaniem odkładań grupowych na role.
Sesje Django po stronie serwera przechowywane w bazie danych. Atrybuty cookie: Secure; HttpOnly; SameSite=Lax. Sesja unieważniana po stronie serwera przy wylogowaniu. Timeout po 15 minutach nieaktywności (konfigurowalne per Dzierżawca, maksimum 8 godzin).
exceptions.exception.approve).tenant_admin, submitter, approver, auditor, itso.Rola Postgres audit_writer ma uprawnienie INSERT na audit_log i brak UPDATE i DELETE. Potok CI/CD weryfikuje ten zestaw uprawnień przy każdym wdrożeniu.
row_hash_n = sha256( prev_hash_(n-1) || canonical_json(row_n) )
Gdzie canonical_json = RFC 8785 JSON Canonicalization Scheme zastosowany do jawnej, wersjonowanej listy pól. Wersja listy pól (schema_version) jest przechowywana w każdym wierszu.
GET /api/audit/verify przechodzi przez pełny łańcuch skrótów dla żądającego Dzierżawcy i zwraca:
{
"ok": true,
"broken_at": null,
"total_rows": 47291,
"first_row_at": "2026-01-15T09:00:00Z",
"last_row_at": "2026-05-15T14:23:11Z",
"last_anchor_at": "2026-05-15T14:00:00Z",
"schema_versions_seen": [1]
}
Zadanie Celery beat uruchamiane co godzinę rejestruje bieżący row_hash najnowszego wiersza per Dzierżawca w tabeli audit_anchor.
Na zweryfikowane żądanie usunięcia (art. 17 RODO), pola actor_email i actor_ip są zastępowane stabilnymi znacznikami (actor_tombstone_<uuid>). łańcuch skrótów jest przeliczany na wartości znacznika, zachowując integralność.
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload.Wszystkie odpowiedzi zawierają nagłówki: Strict-Transport-Security (preload), Content-Security-Policy (oparty na nonce), X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Cross-Origin-Opener-Policy: same-origin, Referrer-Policy: strict-origin-when-cross-origin, Permissions-Policy (odmowa domyślna).
gitleaks działa w CI przy każdym commicie i jako hook pre-commit.cloudflared utrzymuje wychodzące połączenie mTLS.requirements.txt ze skrótami SHA-256. Zależności npm — package-lock.json. Obrazy Docker bazowe referencjonowane przez digest.pip-audit, npm audit, Trivy, Semgrep, gitleaks — w CI i nocnym harmonogramie.| Ważność | Wynik CVSS | SLA naprawy |
|---|---|---|
| Krytyczna | ≥ 9,0 | 72 godziny od potwierdzonej podatności |
| Wysoka | 7,0–8,9 | 14 dni |
| Średnia | 4,0–6,9 | 30 dni |
| Niska | < 4,0 | Następne wydanie mniejsze |
Skoordynowane ujawnianie: security@parakscol.pl. .well-known/security.txt opublikowany na wszystkich domenach marki. Zewnętrzny test penetracyjny planowany przed pierwszym płatnym klientem.
Udokumentowany podręcznik IR: wykrywanie i triage, izolacja, eliminacja, odtwarzanie, post-mortem i powiadamianie regulacyjne.
| Jurysdykcja / prawo | Powiadomienie organu nadzorczego | Powiadomienie osób dotkniętych |
|---|---|---|
| RODO / UE | ≤ 72 godziny od powzięcia wiadomości (art. 33) | Bez zbędnej zwłoki dla naruszeń wysokiego ryzyka (art. 34) |
| Polska (KSC / NIS2) | 24h wczesne ostrzeżenie; 72h zgłoszenie; 30 dni raport końcowy (art. 23 NIS2) | Zgodnie z obowiązującym prawem |
Obowiązek Operatora powiadamiania Administratora (Dzierżawcy) o naruszeniu: ≤ 72 godziny, zgodnie z DPA §8.
Wierzymy w nienadmiarowe twierdzenia. Poniższa lista jest uczciwa w kwestii luk.
| Element | Status | Plan |
|---|---|---|
| Certyfikacja SOC 2 Type II | Jeszcze nie — zaangażowanie audytorskie nie rozpoczęte | Architektura zaprojektowana pod kątem gotowości SOC 2. Zaangażowanie audytora planowane po fazie GA. |
| Certyfikacja ISO 27001 | Jeszcze nie | Planowana po SOC 2 Type II. |
| FedRAMP | Nie dotyczy w v1 | Brak klientów federalnych USA w v1. |
| Zewnętrzny urząd znaczników czasu RFC 3161 | Architektura to obsługuje; jeszcze niepodłączony do produkcyjnego TSA | Faza 2. |
| Cykliczny zewnętrzny test penetracyjny | Pierwszy test planowany przed pierwszym płatnym klientem | Roczna kadencja po osiągnięciu odpowiednich przychodów. |
| Program bug bounty | Jeszcze nie | Faza 2/3 — Intigriti lub HackerOne. |
| Warm-standby DR (RTO ≤ 30 min) | Jeszcze nie — obecny RTO ≤ 4 godziny | Faza 3 — wymaga repliki hot-standby i automatycznego przełączania awaryjnego. |
| Zdalny atest LUKS | [DO UZUPEŁNIENIA: udokumentować produkcyjny mechanizm odblokowania LUKS] | Aktualnie wymaga ręcznego wprowadzania klucza przy restarcie VPS. |
| Cel | Kontakt |
|---|---|
| Ujawnianie luk bezpieczeństwa | security@parakscol.pl |
| Prywatność / DPA | iod@parakscol.pl |
.well-known/security.txt | Opublikowany na każdej domenie marki |
| Klucz publiczny PGP | [DO UZUPEŁNIENIA: opublikować pod adresem .well-known/security.txt] |