161 lines
16 KiB
Markdown
161 lines
16 KiB
Markdown
# Threat model
|
|
|
|
## Scope en assets
|
|
|
|
Te beschermen:
|
|
|
|
- gebruikersaccount, sessies en zoekvoorkeuren;
|
|
- mailbox-, SMTP-, database- en registrycredentials;
|
|
- sollicitatiedossiers, notities en eventuele documenten;
|
|
- integriteit van vacatureclusters, scores en feedback;
|
|
- beschikbaarheid van de Unraid-host en het thuisnetwerk;
|
|
- bronbeleid en bewijs dat alleen toegestane bronnen worden benaderd.
|
|
|
|
Niet vertrouwd:
|
|
|
|
- iedere vacaturetekst, HTML, JSON-LD, feed en e-mail;
|
|
- alle links en redirectdoelen;
|
|
- externe DNS/HTTP/TLS-responses;
|
|
- AI-modeloutput, ook lokaal;
|
|
- browserinput en handmatige URL's;
|
|
- logs of exports die buiten de applicatie worden geopend.
|
|
|
|
## Trust boundaries
|
|
|
|
```text
|
|
Internet/mailbox
|
|
-> URL/source policy + IMAP boundary
|
|
-> raw document quarantine/retention
|
|
-> parser + sanitizer
|
|
-> normalized domain model
|
|
-> deterministic rules/scoring
|
|
-> authenticated UI / digest
|
|
|
|
User browser
|
|
-> TLS/reverse proxy
|
|
-> Django session + CSRF + object scoping
|
|
-> database
|
|
|
|
Django/Celery
|
|
-> Redis (queue, geen waarheid)
|
|
-> PostgreSQL (bron van waarheid)
|
|
-> optionele Ollama boundary
|
|
```
|
|
|
|
## Belangrijkste dreigingen en mitigaties
|
|
|
|
| ID | Dreiging | Impact | Mitigaties | Verificatie |
|
|
|---|---|---|---|---|
|
|
| T-01 | SSRF via bron- of vacature-URL | toegang tot LAN, metadata of beheerinterfaces | schema/host/IP/poortcontrole, DNS-resolutie, redirecthercontrole, denylist, geen credentials in URL | `tests/security/test_url_security.py`, `test_policy.py`, fetchertests |
|
|
| T-02 | DNS rebinding/tijd-van-controle | request bereikt ander IP dan beoordeeld | iedere fetch en redirect valideert DNS opnieuw; het werkelijk verbonden peer-IP wordt als publiek gecontroleerd wanneer de transportlaag dit exposeert, anders geldt conservatieve DNS-set-overlap | `VR-115`, `tests/unit/test_fetcher.py`, aanvullende fetcher negative tests |
|
|
| T-03 | XSS/HTML-injectie | sessiediefstal of ongewenste navigatie | Bleach allowlist, platte tekst, template autoescaping, CSP/securityheaders en uitsluitend lokale externe scripts zonder `unsafe-inline` | `tests/security/test_sanitize.py`, auth- en viewtests |
|
|
| T-04 | Prompt injection in vacature | AI verandert beleid of voert tools uit | vacaturetekst in data-envelope, schema-output, evidence-validatie, cachefouten met foutcategorie, geen toolrechten, deterministische harde regels, AI optioneel | AI-tests, ADR-0003, `VR-109` evaluaties |
|
|
| T-05 | Malafide/oversized document | geheugen/CPU/diskuitputting | contenttype-, byte-, timeout- en redirectlimieten; korte retentie | fetchertests, source-runmetrics |
|
|
| T-06 | Crawlstorm of ban | externe overlast, IP-blokkade | expliciet allowbeleid, per-origin minimuminterval, concurrencylimiet, leasegebaseerde taakclaim met TTL + crash recovery, conditional requests, backoff, quarantaine | tasktests; `VR-103`, `VR-112` |
|
|
| T-07 | Direct platformscrapen | voorwaarden-/blokkaderisico | centrale denylist, e-mailingress, geen loginbot/CAPTCHA | policytests, ADR-0002/0005 |
|
|
| T-08 | E-mailparserlink naar phishing/private host | ongewenste fetch of gebruiker misleid | linkextractie slaat unsubscribe over, follow-up opnieuw door policy/SSRF, bronlabel zichtbaar | e-mail- en policytests |
|
|
| T-09 | MIME/HTML-bom of malformed mail | resource-uitputting/parsercrash | IMAPbatchlimiet, bodylimiet bij vervolgtaak, robuuste parser, idempotente recordidentiteit | `VR-115`, e-mailfixtures |
|
|
| T-10 | Cross-user objecttoegang | lek van sollicitaties/feedback | LoginRequired, user-scoped querysets/get_or_create, autorisatietests | integratie-viewtests |
|
|
| T-11 | CSRF op feedback/profiel | ongewenste status- of profielmutatie | Django CSRF-middleware, POST-only acties, SameSitecookies | viewtests en frameworkcheck |
|
|
| T-12 | Credentiallek in Git/logs/database/UI | account- of infrastructuurcompromis | app-wachtwoorden met MultiFernet en afzonderlijke environmentkey versleuteld; nooit terugtonen; generieke fouttekst; gitignore, secretrotatierunbook | `tests/unit/test_mailbox_credentials.py`, `tests/integration/test_mailbox_connections.py`, CI-secret scan `VR-115` |
|
|
| T-13 | Onveilige reverse proxy | sessie over HTTP, host-header-/originproblemen | TLS, exact ALLOWED_HOSTS/CSRF origins, secure cookies, proxychecklist | deployment smoke-test |
|
|
| T-14 | Redis als waarheid behandeld | dataverlies/dubbele verwerking | PostgreSQL-state, idempotente taken/outbox, acks-late | integratietests |
|
|
| T-15 | Dubbele Celery-uitvoering | dubbele vacatures/mails | unieke keys, transacties, message identity, outbox-uniciteit | pipeline/digesttests |
|
|
| T-16 | Dedupe voegt verschillende jobs samen | gemiste kans/verkeerde data | exact vóór fuzzy, conservatieve drempel, bronaliassen/provenance, herstelbare clusterbeslissing gepland | dedupetests; `VR-116` evaluaties |
|
|
| T-17 | AI hallucineert ontbrekend feit | foutieve ranking | onbekend blijft onbekend, evidence/confidence, geen hard exclusion door AI | scoring/AI-tests |
|
|
| T-18 | Feedback poisoning/self-reinforcement | steeds smallere resultaten | begrensde gewichten, opt-in standaard uit, per-feature minimum samples, metadata-logging en resetpad | profieltests; `VR-110` |
|
|
| T-19 | Automatisch solliciteren/massamail | reputatie/privacy/juridisch risico | geen submitendpoint/task, expliciete ADR en non-goal | codezoekreview, AC-043 |
|
|
| T-20 | Kwaadaardig bestand in sollicitatiedossier | host/browsercompromis | uploads nog niet actief; vóór implementatie allowlist, sniffing, size limit, niet-uitvoerbare opslag vereist | `VR-113`, nieuwe threat review |
|
|
| T-21 | Back-up bevat secrets/PII | langdurig datalek | encryptie, gescheiden opslag, korte retentie, herstelprocedure | runbook en hersteltest |
|
|
| T-22 | Supply-chainpackage/image | code execution | exacte versies/lockfile, beperkte dependencies, pinned baseimages/digests, taggestuurde immutable release als enig productiepad, gescheiden package read/write-tokens en SBOM/checksums | CI, `VR-115`, `VR-230`, `VR-231` |
|
|
| T-23 | Onbevoegde admin op thuisnetwerk | volledige datatoegang | uniek wachtwoord, TLS/VPN, geen defaultcredentials, sessiebeveiliging | Unraidchecklist |
|
|
| T-24 | Source terms wijzigen | ongewenste voortgezette crawling | reviewdatum, source health en policy expiry gepland | `VR-102`, `VR-112` |
|
|
| T-31 | Sollicitatiedossier-export bevat niet-gewenste payload | data-lek of onbedoeld dossieroverdragen | export bevat alleen het gekozen dossier, ZIP-inhoud is beperkt tot snapshot/tijdlijn/print-HTML, bestandsnaam is geslugified zonder padseparators | `tests/integration/test_applications.py` |
|
|
| T-32 | XML external entity/entity-expansion via sitemap | lokale data-uitlezing of parseruitputting | sitemap-XML wordt met `defusedxml` verwerkt; fetcherlimieten blijven vóór parsing gelden | `tests/unit/test_discovery_rss.py`, Ruff securitycheck |
|
|
| T-33 | Externe UI-CDN of niet-vertrouwde Stitch-interactie | supply-chain-XSS, tracking of acties buiten Django-beleid | productie gebruikt alleen lokale CSS/JS en inline SVG-symbolen; Stitch-code is documentatiereferentie, alle mutaties blijven Django POST+CSRF; themavoorkeur bevat geen persoonsgegevens en opslagfouten worden afgevangen | `tests/e2e/test_vr114_browser_and_accessibility.py`, browserconsole- en assetcontrole VR-118 |
|
|
| T-34 | Publieke ATS-feed of robotsredirect wijzigt van host/identiteit | SSRF, verkeerd werkgeverslabel, ongewenst sollicitatie-endpoint of voortgezet fetchen na voorwaardenwijziging | iedere robotsredirect wordt opnieuw als publieke URL gevalideerd; feeds gebruiken alleen gereviewde GET-endpoints; werkgeveridentiteit komt uit source-config; reviews verlopen fail-closed en apply-endpoints zijn geen fetchbron | `tests/security/test_robots.py`, `tests/integration/test_pipeline.py`, `tests/unit/test_ats_adapters.py`, `VR-120` |
|
|
| T-35 | Regionale vacaturehub bevat een gemanipuleerde off-domain kaart of gedeelde ATS-host overschrijft een andere werkgever | phishinglink, foutieve herkomst, verlies van bronbeleid of verkeerde inventaris | regionale adapter accepteert alleen same-host `/job/`-links; bronuniciteit gebruikt type + exacte publieke base-URL; per-origin lease en minimuminterval blijven over alle ATS-tenants gedeeld | `tests/unit/test_regional_adapters.py`, `tests/unit/test_source_models.py`, `tests/integration/test_seed_sources.py`, `VR-121` |
|
|
| T-36 | Kwaadaardige/oversized platformmail of linkinjectie in een jobalert | parseruitputting, batchuitval, phishing, foutieve vacatures of verborgen scraping | IMAP-batch is begrensd; foutisolatie en idempotente identity per mailbox; platformparser accepteert alleen gelabelde HTTPS-links op het gekozen jobboarddomein en verwerpt externe/adminlinks; alle brede jobboards blijven op de directe-fetchdenylist | `tests/integration/test_email_import_notifications.py`, `tests/unit/test_email_adapter.py`, `tests/security/test_policy.py`, `VR-122/123/124/125` |
|
|
| T-37 | Lokale werkgeverslijst wijzigt markup, injecteert een externe kaart of vermengt functies buiten de regionale scope | phishing, foutieve locatie, te brede radar of onbetrouwbare ranking | lokale adapter is beperkt tot vijf gereviewde host/padcombinaties, accepteert alleen same-host details en past waar beschikbaar een expliciete Hasselt-filter toe; retire-seeding schakelt buitenregionale bronnen uit zonder provenance te wissen | `tests/unit/test_regional_adapters.py`, `tests/integration/test_seed_sources.py`, `VR-125` |
|
|
| T-38 | Cross-user mailboxbeheer, dubbele polls of IMAP naar private host | credentialmisbruik, duplicaten, toegang tot interne diensten | alle views scopen op gebruiker en vereisen CSRF; unieke user+platformkoppeling; transactionele lease; hostvorm en publieke DNS/IP-validatie vóór TLS-IMAP | `tests/integration/test_mailbox_connections.py`, `tests/unit/test_mailbox_credentials.py`, `apps/sources/services/imap_import.py`, `VR-123` |
|
|
| T-39 | Mol-/Kempenbron vermengt een andere werkgever, campus of vestiging | foutieve afstand, phishing of buitenregionale resultaten | exacte HTTPS-host/pad/GUID-routes; same-host details; expliciete plaatsfilters voor regionale en gedeelde lijsten; postcodecentra blijven als benadering herkenbaar; waaklijstbronnen worden niet gescand | `tests/unit/test_regional_adapters.py`, `tests/integration/test_tasks_and_more_views.py`, `VR-126/201` |
|
|
| T-40 | Verouderde lokale CA-store of ongeldige serverketen lokt een TLS-bypass uit | onderschepte vacature- of mailboxdata en credentialdiefstal | expliciet gepinde `certifi`-CA-bundel voor HTTP, robots en IMAP; hostnaamcontrole en `CERT_REQUIRED`; nooit `verify=False`; dependency- en negatieve TLS-tests bij update | `tests/unit/test_tls.py`, `tests/security/test_robots.py`, `apps/sources/services/tls.py`, `VR-201` |
|
|
| T-41 | Gemodificeerde `next`-parameter na vacaturefeedback | open redirect naar phishingdomein na een geldige gebruikersactie | terugkeer-URL moet dezelfde host en het actuele HTTPS-beleid respecteren; anders veilige detailfallback | `tests/integration/test_tasks_and_more_views.py`, `VR-206` |
|
|
| T-42 | Gespoofte proxyheaders of ontbrekende publieke hostconfiguratie | host-headerfouten, onjuiste HTTPS-detectie of omzeilde IP-rate-limiting | `PUBLIC_BASE_URL`, exacte host/CSRF-validatie, proxy moet headers overschrijven, forwarded client-IP alleen vanaf `TRUSTED_PROXY_CIDRS` | `tests/unit/test_public_configuration.py`, `tests/unit/test_network.py`, `VR-225` |
|
|
| T-43 | Gedeeld demoaccount wijzigt persistente data of activeert echte mailboxpolling | bezoekers beïnvloeden elkaars ervaring, echte data of externe accounts | server-side read-only middleware, korte sessie, geen bruikbaar wachtwoord, uitsluitend fictieve `example.test`-mailboxen; de loginseed herstelt begrensd en idempotent en de scheduler sluit de demo-user expliciet uit | `tests/integration/test_auth_login.py`, `tests/integration/test_tasks_and_more_views.py`, `tests/unit/test_settings_security.py`, `VR-225/229` |
|
|
|
|
## Misbruikscenario's
|
|
|
|
### Vacature zegt “negeer eerdere instructies”
|
|
|
|
De tekst wordt gesanitized en als veldwaarde doorgegeven. Een eventuele AI-call krijgt een vast schema en geen tools. De output kan geen bron activeren, URL openen, harde regel verwijderen, mail sturen of sollicitatie indienen.
|
|
|
|
### Publieke pagina redirect naar `192.168.1.1`
|
|
|
|
De redirectlocatie wordt opnieuw gevalideerd. Het request naar het private adres wordt niet uitgevoerd; de bronrun krijgt een policy/securityfout en de bron kan in quarantaine gaan.
|
|
|
|
### E-mailalert bevat een platformlink en uitschrijflink
|
|
|
|
De adapter gebruikt de gekozen platformidentiteit, accepteert alleen gelabelde HTTPS-links op dat jobboarddomein en negeert externe tracking-, account-, privacy- en uitschrijflinks. De platformlink wordt hoogstens als alias opgeslagen. Automatisch volgen vereist opnieuw een toegestane source policy; denylistplatformen worden geweigerd.
|
|
|
|
### Aanvaller laat scheduler dezelfde job herhalen
|
|
|
|
Canonieke sleutels, databaseconstraints, transacties, idempotente message identity en outboxuniciteit beperken duplicaten. Taken zijn ontworpen voor at-least-once in plaats van exact-once aannames.
|
|
|
|
## Security gates bij nieuwe functionaliteit
|
|
|
|
Een wijziging aan netwerk, mail, AI, rendering, auth, exports of uploads vereist:
|
|
|
|
1. nieuwe/gewijzigde threat in deze tabel;
|
|
2. negatieve test die de aanval representeert;
|
|
3. begrensde input/output en foutcategorie;
|
|
4. logreview op secrets/PII;
|
|
5. runbook- en configuratie-update;
|
|
6. volledige `codex_verify`-gate.
|
|
|
|
## Resterende risico's
|
|
|
|
- De productiefetcher blokkeert fail-closed wanneer httpcore het werkelijk verbonden
|
|
peer-IP niet exposeert en weigert ieder niet-publiek peer-IP. Alleen een expliciet
|
|
geïnjecteerde test/custom client mag terugvallen op DNS-set-overlap; die clientgrens
|
|
mag daarom niet voor productie-fetches worden gebruikt.
|
|
- Brute-force of herhaalde foutieve mutatiepogingen blijven mogelijk, maar zijn in productie begrensd via gedeelde Redis-rate-limiting en blokkades op login/manual-import. Correcte individuele client-IPherkenning vereist het exacte proxy-CIDR.
|
|
- Bronvoorwaarden vereisen menselijke/externe review per domein; automatisering kan dat niet juridisch beslissen.
|
|
- Fuzzy dedupe en featureheuristieken kunnen inhoudelijk verkeerd zijn; provenance, feedback en benchmarkevaluaties beperken maar elimineren dit niet.
|
|
- Een persoonlijke Unraid-host blijft afhankelijk van correcte netwerksegmentatie, patching en back-ups door de beheerder.
|
|
|
|
- T-25: Verloren of verlopen policyreview
|
|
- Impact: bron kan op onbedoelde wijze doorgaan met crawlen
|
|
- Mitigatie: `SourcePolicyReview` met decision/expiry + policy gate in `assess_url`
|
|
- Verificatie: security/integration tests rond verlopen review in fail-closed gedrag
|
|
|
|
- T-26: Handmatige URL/tekstimport
|
|
- Impact: ongewild ophalen van geblokkeerde of niet-ingesteste informatie, XSS via ingevulde tekst of overschrijding van limieten
|
|
- Mitigatie: CSRF-only import endpoint, allowlist-domeinvrije policy checks, sanitize op tekstveld, byte-limiet en max-size/timeout gedrag
|
|
- Verificatie: integration/security tests rond rejectie van geblokkeerde URL's en overschrijding van paste-limiet
|
|
|
|
- T-27: Reminder-flood of dubbele reminder-mails
|
|
- Impact: gebruiker ontvangt herhaalde of ongewenste herinneringen
|
|
- Mitigatie: stabiele `dedupe_key`, idempotente outbox met statusmachine, `reminder_type`-types en task-driven verzending
|
|
- Verificatie: `tests/integration/test_reminders.py`
|
|
|
|
- T-28: Onterecht verzenden zonder ontvanger/uitgeschakelde reminderinstelling
|
|
- Impact: onnodige fout- of testmails, verwarring over afmeldgedrag
|
|
- Mitigatie: master- en topmatchschakelaar per profiel, `SKIPPED`-status bij lege ontvanger en foutlogica met retries op taakniveau
|
|
- Verificatie: `tests/integration/test_reminders.py`
|
|
|
|
- T-29: Bronhealth zet bronnen te vroeg in quarantaine bij incidentele netwerkruis
|
|
- Impact: tijdelijke uitval of incidentele bronfouten blokkeren een gezonde bron
|
|
- Mitigatie: duidelijke foutcategoriedrempels, gescheiden policy/security- en tijdelijke-foutlogica, en canaryherstel via trialpad
|
|
- Verificatie: `tests/integration/test_source_health.py`
|
|
|
|
- T-30: Geautomatiseerde canaryherstel blijft onbegrensd draaien
|
|
- Impact: herhaalde herstarts van dezelfde bron veroorzaken extra belasting
|
|
- Mitigatie: cooldown plus metadataflag voor eenmalige canary, en trialstatus met beperkte runtrigger
|
|
- Verificatie: `tests/integration/test_source_health.py`
|
|
# Microsoft 365 OAuth
|
|
|
|
- Exchange Online gebruikt app-only XOAUTH2; Basic Auth en app-wachtwoorden zijn uitgesloten.
|
|
- Client secrets leven alleen in de productie-secretstore en tokenfouten bevatten geen token of secret.
|
|
- De serviceprincipal krijgt uitsluitend IMAP/SMTP-toegang tot de bedoelde mailbox; IMAP-selectie blijft read-only.
|