Security Audit
Audit Informatie
Section titled “Audit Informatie”| Datum | April 2026 |
| Scope | 53 PHP bestanden in werkbon thema |
Samenvatting
Section titled “Samenvatting”| Ernst | Aantal |
|---|---|
| Kritiek | 1 |
| Hoog | 4 |
| Medium | 4 |
| Laag | 2 |
Kritieke Bevindingen
Section titled “Kritieke Bevindingen”FINDING-01: REST API zonder authenticatie
Section titled “FINDING-01: REST API zonder authenticatie”Ernst: KRITIEK Bestanden: api-contact.php:392, api-job.php:25, api-file.php:289
function acs_permission_contacts(): bool { return true; // GEEN AUTH CHECK}Alle 3 API bestanden:
// api-contact.php:391function acs_permission_contacts(): bool { return true; // return is_user_logged_in() && current_user_can('edit_posts');}
// api-job.php:25function acs_job_api_permission(): bool { return true;}
// api-file.php:289'permission_callback' => '__return_true',Reproductie:
# Dump alle contacten met persoonsgegevenscurl -s 'https://werkbon.nl/wp-json/acs/v1/contacts?per_page=100' | jq '.items | length'
# Maak ongeautoriseerd een melding aancurl -X POST 'https://werkbon.nl/wp-json/acs/v1/jobs/create' \ -H 'Content-Type: application/json' \ -d '{"description":"test","email":"test@test.nl"}'Impact:
GET /wp-json/acs/v1/contactsdumpt alle persoonsgegevens (namen, emails, telefoons, adressen)POST /wp-json/acs/v1/contacts/upsert— iedereen kan contacten wijzigenPOST /wp-json/acs/v1/jobs/create— iedereen kan meldingen aanmaken- AVG/GDPR overtreding — Artikel 32 (beveiliging), Artikel 5 lid 1f (integriteit en vertrouwelijkheid)
Aanbeveling:
function acs_permission_contacts(): bool { $secret = $_SERVER['HTTP_X_ACS_SECRET'] ?? ''; if ($secret && hash_equals(WERKBON_API_SECRET, $secret)) return true; return is_user_logged_in() && current_user_can('edit_posts');}Geschatte tijd: 1 uur
Hoge Bevindingen
Section titled “Hoge Bevindingen”FINDING-02: Raw $_GET in pagination URLs
Section titled “FINDING-02: Raw $_GET in pagination URLs”Bestanden: show-reports.php:725, show-jobs.php:558
$query_args = $_GET; // ONGESANITEERDXSS vector via geinjecteerde query parameters. Fix: whitelist + sanitize_text_field().
Geschatte tijd: 30 min
FINDING-03: Ongeescapete permalink output
Section titled “FINDING-03: Ongeescapete permalink output”Bestanden: show-reports.php:81, show-jobs.php:105, show-dashboard.php:45-53
<a href="<?php echo get_the_permalink(2); ?>">Moet: esc_url(get_the_permalink(2))
Geschatte tijd: 15 min
FINDING-04: Debug logging in productie
Section titled “FINDING-04: Debug logging in productie”Bestand: functions.php:4
error_log('LOADED FUNCTIONS: ' . __FILE__);Bij elke pageload. Log flooding + informatie disclosure.
Geschatte tijd: 5 min
FINDING-05: AJAX missing explicit login check
Section titled “FINDING-05: AJAX missing explicit login check”Bestand: modal-jobs.php:178
update_job_status mist expliciete is_user_logged_in() check. Impliciet via current_user_can maar inconsistent met delete_job handler.
Geschatte tijd: 5 min
Medium Bevindingen
Section titled “Medium Bevindingen”FINDING-06: ABSPATH guard ontbreekt (43/53 bestanden)
Section titled “FINDING-06: ABSPATH guard ontbreekt (43/53 bestanden)”43 van 53 PHP bestanden missen if (!defined('ABSPATH')) exit;. Directe toegang kan pad-informatie lekken via PHP errors.
Geschatte tijd: 30 min
FINDING-07: Sessie cookie 30 dagen zonder Remember Me
Section titled “FINDING-07: Sessie cookie 30 dagen zonder Remember Me”Bestand: login.php:44
$remember parameter wordt genegeerd. Op gedeelde apparaten onverwacht lang actief.
Geschatte tijd: 10 min
FINDING-08: Webhook secrets in broncode
Section titled “FINDING-08: Webhook secrets in broncode”Bestanden: api-file.php:13, api-contact.php:10
Make.com webhook URLs met tokens hardcoded en in Git.
Geschatte tijd: 30 min
FINDING-09: Raw SVG output
Section titled “FINDING-09: Raw SVG output”Bestanden: navigation.php:38, show-login.php:7
file_get_contents() op SVG zonder sanitatie. SVGs kunnen <script> tags bevatten.
Geschatte tijd: 15 min
Lage Bevindingen
Section titled “Lage Bevindingen”FINDING-10: Glob include pattern
Section titled “FINDING-10: Glob include pattern”Bestand: functions.php:7
Elk bestand in /functions/ wordt auto-executed. Risico bij schrijftoegang.
FINDING-11: nopriv AJAX hook
Section titled “FINDING-11: nopriv AJAX hook”Bestand: form-addresses.php:41
Address lookup voor niet-ingelogde gebruikers. Data niet gevoelig maar onnodige exposure.
FINDING-12: CSRF op form prefill URLs
Section titled “FINDING-12: CSRF op form prefill URLs”Ernst: LAAG Bestand: form-prefill.php
Form prefill parameters (?job_id=123, ?wpf40_58=123, ?wpf40_74=maasdelta) worden zonder nonce-verificatie gelezen uit $_GET. Een aanvaller kan een URL crafting die het formulier voorvult met specifieke waarden.
Impact: Laag — de monteur moet het formulier nog steeds zelf submitten. Maar het kan misleidend zijn als de prefill waarden onverwacht zijn.
Reproductie:
https://werkbon.nl/werkbon-invullen/?wpf40_74=maasdelta&wpf40_68=Fake+CompanyMigratie Impact
Section titled “Migratie Impact”Bij migratie naar de Beam stack (Supabase + Hono API) worden de volgende findings automatisch opgelost:
| Finding | Opgelost door | Reden |
|---|---|---|
| FINDING-01 (open API) | Hono auth middleware | Bearer token vereist op alle routes |
| FINDING-02 (raw $_GET) | React Router | Geen server-side URL building |
| FINDING-03 (unescaped permalink) | React JSX | Automatische escaping |
| FINDING-04 (debug logging) | Geen functions.php | Structured logging via middleware |
| FINDING-06 (ABSPATH guard) | Geen PHP bestanden | TypeScript modules |
| FINDING-07 (sessie 30d) | Supabase Auth | Configureerbare sessie duur |
| FINDING-08 (webhook secrets) | Wrangler secrets | Environment variables, niet in code |
| FINDING-09 (raw SVG) | React components | Geen file_get_contents |
| FINDING-10 (glob include) | ES modules | Expliciete imports |
| FINDING-11 (nopriv AJAX) | Auth middleware | Alle routes geauthenticeerd |
| FINDING-12 (CSRF prefill) | React state | Geen URL-gebaseerde prefill |
Zie Migratie naar Beam Stack voor de volledige migratie analyse.
Risicomatrix
Section titled “Risicomatrix”| Finding | Waarschijnlijkheid | Impact | Risico |
|---|---|---|---|
| F-01: Open API | Hoog (publiek bereikbaar) | Kritiek (PII exposure) | Kritiek |
| F-02: Raw $_GET | Medium (vereist crafted URL) | Hoog (XSS) | Hoog |
| F-03: Unescaped permalink | Laag (vereist admin compromise) | Medium (XSS) | Medium |
| F-04: Debug logging | Hoog (elke pageload) | Laag (info disclosure) | Medium |
| F-05: Missing login check | Laag (impliciet beschermd) | Medium (privilege escalation) | Laag |
| F-06: ABSPATH guards | Laag (vereist directe URL) | Laag (path disclosure) | Laag |
| F-07: 30d sessie | Hoog (altijd actief) | Medium (session hijacking) | Medium |
| F-08: Webhook secrets | Medium (repo access nodig) | Hoog (webhook abuse) | Hoog |
| F-09: Raw SVG | Laag (vereist fs access) | Medium (XSS) | Laag |
| F-10: Glob include | Laag (vereist fs access) | Hoog (RCE) | Medium |
| F-11: nopriv AJAX | Hoog (publiek) | Laag (publieke data) | Laag |
| F-12: CSRF prefill | Hoog (publiek) | Laag (misleiding) | Laag |
Compliance Checklist
Section titled “Compliance Checklist”AVG/GDPR
Section titled “AVG/GDPR”| Vereiste | Status | Finding |
|---|---|---|
| Art. 5(1)(f) — Integriteit en vertrouwelijkheid | Niet voldaan | F-01 |
| Art. 25 — Data protection by design | Niet voldaan | F-01 |
| Art. 32 — Beveiliging van verwerking | Niet voldaan | F-01, F-08 |
| Art. 33 — Melding datalek aan AP | N.v.t. | Geen actief lek bekend |
| Art. 35 — DPIA (Data Protection Impact Assessment) | Niet uitgevoerd | Aanbevolen |
OWASP Top 10 (2021)
Section titled “OWASP Top 10 (2021)”| Categorie | Status | Finding |
|---|---|---|
| A01: Broken Access Control | Niet voldaan | F-01 |
| A02: Cryptographic Failures | Voldaan | — |
| A03: Injection (SQL) | Voldaan | $wpdb->prepare() |
| A04: Insecure Design | Deels | F-07, F-10 |
| A05: Security Misconfiguration | Deels | F-04, F-06 |
| A06: Vulnerable Components | Onbekend | Dependencies niet geaudit |
| A07: Auth Failures | Deels | F-01, F-05 |
| A08: Data Integrity Failures | Voldaan | Nonces op forms |
| A09: Logging Failures | Deels | F-04 (overmatig) |
| A10: SSRF | Voldaan | Geen user-controlled URLs |
Positieve Bevindingen
Section titled “Positieve Bevindingen”| Aspect | Status |
|---|---|
| Input sanitization ($_GET) | Goed — sanitize_text_field(), absint(), in_array() |
| SQL injection ($wpdb->prepare) | Goed — consistent gebruikt |
| AJAX nonce verificatie | Goed — check_ajax_referer() |
| Admin form bescherming | Goed — wp_nonce_field + check_admin_referer |
| Output escaping (templates) | Goed — esc_attr(), esc_html() |
| Capability checks | Goed — current_user_can() |
| Geen eval/unserialize | Goed |
Prioriteiten
Section titled “Prioriteiten”| # | Bevinding | Ernst | Tijd |
|---|---|---|---|
| 1 | REST API authenticatie | Kritiek | 1 uur |
| 2 | $_GET whitelist pagination | Hoog | 30 min |
| 3 | Webhook secrets naar env | Medium | 30 min |
| 4 | Permalink escaping | Hoog | 15 min |
| 5 | Debug logging verwijderen | Hoog | 5 min |
| 6 | 30-dagen sessie fixen | Medium | 10 min |
| 7 | ABSPATH guards | Medium | 30 min |
| 8 | SVG sanitatie | Medium | 15 min |
| 9 | Glob include filter | Laag | 10 min |
| 10 | nopriv AJAX verwijderen | Laag | 5 min |