90 lines
3.4 KiB
Markdown
90 lines
3.4 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
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
|