Files
leerdoelen_tracker/SECURITY.md
Sam 07bcfede75
Some checks failed
Build, Push & Deploy / Build & Push image (push) Failing after 56s
Build, Push & Deploy / Deploy naar VPS (push) Has been skipped
Build & Push / Build & Push image (push) Successful in 1m2s
Add more security and audit
2026-02-28 14:47:33 +01:00

3.4 KiB

Security beleid

Kwetsbaarheden melden

Gevonden een beveiligingsprobleem? Stuur een e-mail naar de systeembeheerder van jouw scholengroep.
Voeg zo veel mogelijk detail toe: stappen om te reproduceren, impact, en eventueel een proof-of-concept.

Publiceer kwetsbaarheden niet publiek voordat ze zijn opgelost.


Beveiligingsmaatregelen in deze applicatie

Authenticatie

  • Primaire login via Microsoft Entra ID (Azure AD) — geen wachtwoorden opgeslagen voor gewone gebruikers
  • Superadmin wachtwoord gehasht met scrypt (sterk geheugenintensief algoritme)
  • OAuth2 state parameter validatie — beschermt tegen CSRF in OAuth flow
  • ?next= redirect parameter gevalideerd — beschermt tegen open redirect aanvallen
  • Session cookies: HttpOnly, Secure (HTTPS), SameSite=Lax

Rate limiting

Endpoint Limiet
Alle /auth/* routes 10 per minuut per IP
Superadmin login 10/min + 30/uur per IP
API endpoints 120 per minuut per IP
Doelen upload 5 per minuut per IP
Setup endpoint 5 per minuut per IP

Rate limiting via Redis (persistent over meerdere workers).
Nginx voegt een extra laag rate limiting toe vóór Flask.

HTTP Security headers

Via Flask-Talisman + Nginx:

  • Content-Security-Policy — nonce-based, geen unsafe-inline scripts
  • Strict-Transport-Security — HSTS 1 jaar, incl. subdomains
  • X-Frame-Options: DENY — clickjacking preventie
  • X-Content-Type-Options: nosniff
  • Referrer-Policy: strict-origin-when-cross-origin
  • Permissions-Policy — geen toegang tot camera/microfoon/locatie
  • form-action: 'self' — voorkomt form hijacking
  • base-uri: 'self' — voorkomt base tag injection
  • object-src: 'none' — geen Flash of plugins

Autorisatie

  • Rolgebaseerde toegangscontrole (superadmin → scholengroep_ict → school_ict → director → teacher)
  • Elke API route heeft expliciete rolauthenticatie decorator
  • School-isolatie: gebruikers kunnen enkel data van hun eigen school zien
  • Auditlog van alle beheerhandelingen

Database

  • Parameterized queries via SQLAlchemy ORM — geen raw SQL met gebruikersinput
  • Non-root database gebruiker
  • PostgreSQL container niet publiek blootgesteld (intern Docker netwerk)

Infrastructuur

  • Flask draait als non-root gebruiker (appuser) in Docker container
  • Read-only volume mount voor doelen JSON bestanden
  • Redis beveiligd met wachtwoord
  • Backend enkel bereikbaar via 127.0.0.1 (niet publiek)
  • Nginx als reverse proxy met request size limiting en timeouts (Slowloris bescherming)

Dependency updates

Controleer regelmatig op kwetsbaarheden in dependencies:

pip install pip-audit
pip-audit -r backend/requirements.txt

Python base image: pin op specifieke patch versie in Dockerfile.
Controleer updates op: https://hub.docker.com/_/python


Checklist voor nieuwe deployment

  • SECRET_KEY gegenereerd met python3 -c "import secrets; print(secrets.token_hex(32))"
  • POSTGRES_PASSWORD sterk en uniek
  • REDIS_PASSWORD ingesteld
  • BASE_URL correct ingesteld op HTTPS URL
  • SSL/TLS certificaat aanwezig (Let's Encrypt via Certbot)
  • Microsoft Entra ID app registratie correct geconfigureerd
  • Superadmin wachtwoord ingesteld via /auth/setup (min. 12 tekens)
  • /auth/setup endpoint niet meer toegankelijk na setup (wordt automatisch geblokkeerd)
  • Firewall: enkel poorten 80 en 443 publiek open