BusiKM — Dokumentacja bezpieczeństwa

1. Uwierzytelnianie JWT

Przepływ logowania

System BusiKM wykorzystuje dwutokenowy mechanizm JWT (JSON Web Token) oparty na bibliotece djangorestframework-simplejwt:

Użytkownik                  Backend                    Redis (DB2)
    |                          |                          |
    |-- POST /api/auth/login ->|                          |
    |                          |-- Weryfikacja danych     |
    |                          |-- Generowanie tokenów    |
    |<-- access + refresh -----|                          |
    |                          |                          |
    |-- request + access ----->|                          |
    |                          |-- Walidacja tokenu       |
    |<-- odpowiedź ------------|                          |
    |                          |                          |
    |-- POST /api/auth/refresh |                          |
    |   + refresh token ------>|                          |
    |                          |-- Rotacja tokenu ------->|-- Blacklist starego
    |<-- nowy access + refresh |                          |

Parametry tokenów

ParametrWartośćOpis
Access token TTL15 minutKrótki czas życia minimalizuje ryzyko przejęcia
Refresh token TTL7 dniUmożliwia dłuższe sesje bez ponownego logowania
AlgorytmHS256HMAC z kluczem symetrycznym (SECRET_KEY)
Rotacja refreshWłączonaKażde odświeżenie generuje nowy refresh token
BlacklistingRedis DB2Unieważnione tokeny przechowywane w Redis

Rotacja tokenów

Po każdym użyciu refresh tokena:

  1. Stary refresh token trafia na blacklistę w Redis DB2.
  2. Generowany jest nowy pair access + refresh.
  3. Klient otrzymuje oba nowe tokeny.

Zapobiega to ponownemu użyciu skradzionego refresh tokena.

Mechanizm token_version — wylogowanie ze wszystkich urządzeń

Każdy użytkownik posiada pole token_version w modelu. Przy operacji „wyloguj ze wszystkich urządzeń":

  1. token_version jest inkrementowany.
  2. Wszystkie istniejące tokeny zawierają starą wersję.
  3. Middleware porównuje token_version z tokenu z aktualnym w bazie.
  4. Niezgodność wersji powoduje odrzucenie tokenu (HTTP 401).

2. Kontrola dostępu oparta na rolach (RBAC)

Role systemowe

RolaIdentyfikatorOpis
KierowcadriverRejestracja tras, podgląd własnych danych
Właściciel firmyownerPełny dostęp do danych firmy, zarządzanie użytkownikami
KsięgowyaccountantDostęp do danych finansowych i raportów firmy
Biuro rachunkoweaccounting_firmObsługa wielu firm klientów z różnym poziomem dostępu

Klasy uprawnień (Permission Classes)

KlasaOpis
IsDriverDostęp tylko dla kierowców
IsOwnerDostęp tylko dla właścicieli firm
IsAccountantDostęp tylko dla księgowych
IsAccountingFirmDostęp tylko dla biur rachunkowych
IsDriverOrOwnerDostęp dla kierowców i właścicieli
IsOwnerOrAccountantOrAFDostęp dla właścicieli, księgowych i biur rachunkowych
IsAccountantOrAFDostęp dla księgowych i biur rachunkowych

ActionPermissionMixin

class TripViewSet(ActionPermissionMixin, viewsets.ModelViewSet):
    action_permissions = {
        'list':     [IsAuthenticated, IsDriverOrOwner],
        'create':   [IsAuthenticated, IsDriver],
        'retrieve': [IsAuthenticated, IsOwnTrip],
        'export':   [IsAuthenticated, IsOwnerOrAccountantOrAF],
    }

3. Izolacja danych firmowych

CompanyScopedMixin

class CompanyScopedMixin:
    def get_queryset(self):
        return super().get_queryset().filter(
            company_id=self.request.user.company_id
        )

Zasada 404 zamiast 403

Gdy użytkownik próbuje uzyskać dostęp do obiektu należącego do innej firmy, system zwraca HTTP 404 (Not Found), a nie 403 (Forbidden). Dzięki temu:

  • Atakujący nie wie, czy obiekt istnieje.
  • Nie można enumerować zasobów innych firm.
  • Brak wycieku informacji o strukturze danych.

Pokrycie testami

Izolacja firmowa jest zabezpieczona ponad 50 testami weryfikującymi brak dostępu do obiektów cudzej firmy, poprawność filtrowania list, zwracanie 404 i brak możliwości modyfikacji danych innej firmy.


4. Uprawnienia na poziomie obiektu

KlasaZastosowanie
IsOwnTripKierowca widzi i edytuje tylko swoje trasy
IsOwnVehicleKierowca widzi tylko pojazdy przypisane do niego
IsOwnCompanyUżytkownik może modyfikować tylko dane swojej firmy
class IsOwnTrip(BasePermission):
    def has_object_permission(self, request, view, obj):
        if request.user.role == 'driver':
            return obj.driver_id == request.user.id
        return True  # Właściciele widzą wszystkie trasy firmy

5. Izolacja multi-tenant dla biur rachunkowych

Architektura

Biuro rachunkowe
    |
    |-- Klient A (full access)
    |-- Klient B (read_only)
    |-- Klient C (reports_only)

Poziomy dostępu

PoziomOpisDozwolone operacje
fullPełny dostępOdczyt, zapis, edycja, eksport, raporty
read_onlyTylko odczytPrzeglądanie danych, bez modyfikacji
reports_onlyTylko raportyGenerowanie i pobieranie raportów

6. Ograniczanie liczby zapytań (Rate Limiting)

Warstwa globalna (Middleware)

Typ użytkownikaLimitOkno czasowe
Anonimowy100 zapytań1 minuta
Uwierzytelniony300 zapytań1 minuta

Implementacja oparta na algorytmie sliding window w Redis.

Warstwa DRF (Throttle Classes)

Klasa throttleLimitZakresZastosowanie
AnonRateThrottle30/minPer IPEndpointy publiczne
UserRateThrottle120/minPer userEndpointy uwierzytelnione
LoginRateThrottle5/minPer emailLogowanie (ochrona brute force)
GPSUploadThrottle10/minPer userUpload danych GPS
ExportThrottle5/minPer userGenerowanie eksportów/raportów

7. Ochrona przed atakami brute force

Próba logowania
    |
    |-- Sprawdzenie limitu per IP -----> Przekroczony? -> 429
    |
    |-- Sprawdzenie limitu per email --> Przekroczony? -> 429
    |
    |-- Weryfikacja danych logowania
    |       |
    |       |-- Niepoprawne -> Log + inkrementacja liczników
    |       |
    |       |-- Poprawne -> Reset liczników + wydanie tokenów

8. Zgodność z OWASP Top 10

IDKategoriaStatusImplementacja
A01Broken Access ControlZabezpieczoneRBAC, CompanyScopedMixin, uprawnienia obiektowe, 50+ testów izolacji, 404 zamiast 403
A02Cryptographic FailuresZabezpieczoneJWT z HS256, hasła hashowane (bcrypt/argon2), HTTPS everywhere, brak PII w logach
A03InjectionZabezpieczoneDjango ORM (parametryzowane zapytania), walidacja serializatorów DRF, brak raw SQL
A04Insecure DesignZabezpieczoneWielowarstwowa architektura bezpieczeństwa, zasada najmniejszych uprawnień, code review
A05Security MisconfigurationZabezpieczoneDEBUG=False w produkcji, usunięty domyślny SECRET_KEY, CSP headers, HSTS
A06Vulnerable ComponentsZabezpieczonepip-audit, npm audit, Dependabot, bandit SAST, regularne aktualizacje
A07Identification and Authentication FailuresZabezpieczoneJWT z rotacją, brute force protection, token_version, blacklisting
A08Software and Data Integrity FailuresZabezpieczonePodpisane tokeny JWT, walidacja MIME plików, CI/CD z kontrolą integralności
A09Security Logging and Monitoring FailuresZabezpieczoneLogowanie nieudanych logowań, rate limit events, structured logging
A10Server-Side Request Forgery (SSRF)ZabezpieczoneBrak endpointów pobierających URL od użytkownika, walidacja uploadu plików

9. Bezpieczeństwo transportu

HTTPS

  • Cała komunikacja odbywa się wyłącznie przez HTTPS.
  • Konfiguracja SECURE_SSL_REDIRECT = True wymusza przekierowanie HTTP -> HTTPS.

HSTS

SECURE_HSTS_SECONDS = 31536000        # 1 rok
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True

Bezpieczne ciasteczka

SESSION_COOKIE_SECURE = True      # Tylko przez HTTPS
CSRF_COOKIE_SECURE = True         # Tylko przez HTTPS
SESSION_COOKIE_HTTPONLY = True     # Niedostępne z JavaScript

10. Polityka CORS

ŚrodowiskoDozwolone origin
Developmenthttp://localhost:3000, http://localhost:8081
Staginghttps://staging.busikm.pl
Produkcjahttps://app.busikm.pl, https://busikm.pl

11. Bezpieczeństwo uploadu plików

Walidacja wielopoziomowa

  1. Walidacja rozszerzenia — dozwolone tylko określone rozszerzenia plików.
  2. Walidacja MIME type — sprawdzenie nagłówka Content-Type.
  3. Walidacja magic bytes — odczyt pierwszych bajtów pliku w celu weryfikacji rzeczywistego formatu.
  4. Limit rozmiaru — maksymalnie 10–15 MB w zależności od typu pliku.

Ochrona przed XSS przez pliki

Wszystkie pliki serwowane z nagłówkiem Content-Disposition: attachment.

Przechowywanie plików

  • Pliki przechowywane w prywatnym bucket S3 (brak publicznego dostępu).
  • Dostęp do plików wyłącznie przez presigned URLs z ograniczonym czasem ważności.

12. Zarządzanie sekretami

ŚrodowiskoMechanizmOpis
Backend (dev/prod)Plik .envNigdy nie commitowany do repozytorium (w .gitignore)
Aplikacja mobilnaEAS SecretsSzyfrowane zmienne środowiskowe Expo Application Services
CI/CDGitHub SecretsSzyfrowane zmienne dostępne w GitHub Actions
ProdukcjaZmienne środowiskoweUstawiane bezpośrednio na platformie hostingowej

13. Bezpieczeństwo warstwy webowej

Ciasteczka httpOnly dla JWT

  • Token niedostępny z poziomu JavaScript (ochrona przed XSS).
  • Automatycznie dołączany do zapytań przez przeglądarkę.
  • Flagi Secure i SameSite=Lax.

Wzorzec BFF (Backend for Frontend)

  1. Klient webowy komunikuje się z BFF na tej samej domenie.
  2. BFF zarządza tokenami JWT (przechowuje w httpOnly cookies).
  3. BFF przekazuje zapytania do API, dołączając token w nagłówku Authorization.
  4. Eliminuje to konieczność przechowywania tokenów w localStorage/sessionStorage.

Nagłówki bezpieczeństwa

# Content Security Policy
CSP_DEFAULT_SRC = ("'self'",)
CSP_SCRIPT_SRC = ("'self'",)
CSP_STYLE_SRC = ("'self'", "'unsafe-inline'")
CSP_IMG_SRC = ("'self'", "data:", "https:")

# Pozostałe nagłówki
X_FRAME_OPTIONS = 'DENY'                    # Ochrona przed clickjacking
SECURE_CONTENT_TYPE_NOSNIFF = True           # Blokada MIME sniffing
SECURE_BROWSER_XSS_FILTER = True             # Filtr XSS przeglądarki

14. Bezpieczeństwo aplikacji mobilnej

SecureStore (Expo)

  • Na iOS: Keychain (szyfrowanie sprzętowe).
  • Na Android: Keystore + EncryptedSharedPreferences.
  • Dane niedostępne dla innych aplikacji.

15. Skanowanie zależności i analiza statyczna

NarzędzieTypJęzykZastosowanie
banditSASTPythonAnaliza statyczna kodu pod kątem podatności
pip-auditSCAPythonSkanowanie zależności pip pod kątem znanych CVE
npm auditSCAJavaScriptSkanowanie zależności npm pod kątem znanych CVE
eslint-plugin-securitySASTJavaScriptReguły ESLint wykrywające niebezpieczne wzorce
DependabotSCAWszystkieAutomatyczne PR z aktualizacjami bezpieczeństwa

Polityka reakcji na podatności

  • Krytyczne (Critical) — natychmiastowa naprawa, deploy w ciągu 24h.
  • Wysokie (High) — naprawa w bieżącym sprincie.
  • Średnie (Medium) — naprawa w kolejnym sprincie.
  • Niskie (Low) — umieszczenie w backlogu.

Podsumowanie

Bezpieczeństwo BusiKM opiera się na wielowarstwowej architekturze ochrony:

  1. Warstwa transportu — HTTPS, HSTS, bezpieczne ciasteczka.
  2. Warstwa uwierzytelniania — JWT z rotacją, blacklisting, token_version.
  3. Warstwa autoryzacji — RBAC, uprawnienia obiektowe, ActionPermissionMixin.
  4. Warstwa izolacji danych — CompanyScopedMixin, TenantContextMiddleware.
  5. Warstwa ochrony przed nadużyciem — rate limiting, brute force protection.
  6. Warstwa przechowywania — szyfrowane tokeny (SecureStore/httpOnly), prywatny S3.
  7. Warstwa CI/CD — automatyczne skanowanie SAST/SCA, Dependabot.

Każda warstwa działa niezależnie — kompromitacja jednej nie oznacza kompromitacji całego systemu (zasada defense in depth).