De live webhost stuurt geen beveiligingsheaders (ZAP/DAST) #849

Closed
opened 2026-07-25 09:06:32 +00:00 by brenno · 3 comments
Owner

Wat
De live webhost (https://ocideck.librekat.nl, Apache) stuurt geen enkele beveiligings-responseheader mee. Een OWASP ZAP-basisscan (make dast DAST_URL=https://ocideck.librekat.nl/) meldt dit als 7 losse waarschuwingen die allemaal dezelfde oorzaak hebben; een kale curl -I bevestigt het:

$ curl -sSI https://ocideck.librekat.nl/
HTTP/1.1 200 OK
Server: Apache
Content-Type: text/html
... (geen CSP, geen X-Frame-Options, geen HSTS, geen nosniff, geen Permissions-Policy)

Waarom dit telt
web/index.html documenteert al bewust dat een via <meta> geleverde CSP frame-ancestors (en sandbox/report-*) niet kan afdwingen — de browser negeert die buiten een echte header. Precies daarom staat er in de bundel: "to stop clickjacking the STATIC HOST must send the CSP (and ideally X-Frame-Options)". De host doet dat nu niet, dus die afdwinging valt weg. De meta-CSP dekt de scriptbron wél; wat ontbreekt is alles wat alléén als header kan.

Wat ZAP vond op de live host (0 FAIL, passief)

ZAP-regel Header die ontbreekt Advies
10020 X-Frame-Options (+ CSP frame-ancestors als header) DENY — sluit clickjacking
10021 X-Content-Type-Options nosniff
10035 Strict-Transport-Security bv. max-age=31536000; includeSubDomainslet op: preload/lange max-age is sticky, bewust kiezen
10038 Content-Security-Policy als header dezelfde policy als de meta-tag, nu mét afdwingbare frame-ancestors 'none'
10055 (gevolg) meta-CSP bevat frame-ancestors die in meta genegeerd wordt lost op zodra CSP als header komt
10063 Permissions-Policy minimaal profiel, bv. camera/microfoon/geolocatie uit
90004 Cross-Origin-Embedder-Policy require-corpnuance: kan cross-origin resources breken, eerst testen

Voorgestelde route (repo-gestuurd)
Een .htaccess in web/ die Flutter naar build/web/ kopieert en die deploy_web.sh meeneemt naar de webroot, met mod_headers-regels. Dan staat de hardening in de repo naast de bundel in plaats van los in serverconfig — consistent met de rest van de deploy. Voorwaarde: AllowOverride moet voor die map aan staan op de Apache-host; staat dat uit, dan hoort het in de vhost-config (buiten deze repo). Die keuze hoort in het issue thuis.

Bewust géén issue (getrieerd, niet gemist)

  • 10027 "Suspicious Comments" — vals-positief. De triggerwoorden (from, select, user) komen uit de geminificeerde main.dart.js, niet uit echt commentaar; web/index.html en flutter_bootstrap.js bevatten er geen. Kandidaat voor zap/baseline.conf op IGNORE.
  • 10015 "Re-examine Cache-control" — Apache-standaard op de HTML; adviserend, geen lek.

Reproduceren

make dast DAST_URL=https://ocideck.librekat.nl/

Scanner: OWASP ZAP stable (baseline, passief) via make dast. Uitgevoerd 2026-07-25.

**Wat** De live webhost (`https://ocideck.librekat.nl`, Apache) stuurt geen enkele beveiligings-responseheader mee. Een OWASP ZAP-basisscan (`make dast DAST_URL=https://ocideck.librekat.nl/`) meldt dit als 7 losse waarschuwingen die allemaal dezelfde oorzaak hebben; een kale `curl -I` bevestigt het: ``` $ curl -sSI https://ocideck.librekat.nl/ HTTP/1.1 200 OK Server: Apache Content-Type: text/html ... (geen CSP, geen X-Frame-Options, geen HSTS, geen nosniff, geen Permissions-Policy) ``` **Waarom dit telt** `web/index.html` documenteert al bewust dat een via `<meta>` geleverde CSP `frame-ancestors` (en `sandbox`/`report-*`) niet kan afdwingen — de browser negeert die buiten een echte header. Precies daarom staat er in de bundel: "to stop clickjacking the STATIC HOST must send the CSP (and ideally X-Frame-Options)". De host doet dat nu niet, dus die afdwinging valt weg. De meta-CSP dekt de scriptbron wél; wat ontbreekt is alles wat alléén als header kan. **Wat ZAP vond op de live host (0 FAIL, passief)** | ZAP-regel | Header die ontbreekt | Advies | |---|---|---| | 10020 | `X-Frame-Options` (+ CSP `frame-ancestors` als header) | `DENY` — sluit clickjacking | | 10021 | `X-Content-Type-Options` | `nosniff` | | 10035 | `Strict-Transport-Security` | bv. `max-age=31536000; includeSubDomains` — **let op**: preload/lange max-age is sticky, bewust kiezen | | 10038 | `Content-Security-Policy` als header | dezelfde policy als de meta-tag, nu mét afdwingbare `frame-ancestors 'none'` | | 10055 | (gevolg) meta-CSP bevat `frame-ancestors` die in meta genegeerd wordt | lost op zodra CSP als header komt | | 10063 | `Permissions-Policy` | minimaal profiel, bv. camera/microfoon/geolocatie uit | | 90004 | `Cross-Origin-Embedder-Policy` | `require-corp` — **nuance**: kan cross-origin resources breken, eerst testen | **Voorgestelde route (repo-gestuurd)** Een `.htaccess` in `web/` die Flutter naar `build/web/` kopieert en die `deploy_web.sh` meeneemt naar de webroot, met `mod_headers`-regels. Dan staat de hardening in de repo naast de bundel in plaats van los in serverconfig — consistent met de rest van de deploy. Voorwaarde: `AllowOverride` moet voor die map aan staan op de Apache-host; staat dat uit, dan hoort het in de vhost-config (buiten deze repo). Die keuze hoort in het issue thuis. **Bewust géén issue (getrieerd, niet gemist)** - **10027 "Suspicious Comments"** — vals-positief. De triggerwoorden (`from`, `select`, `user`) komen uit de geminificeerde `main.dart.js`, niet uit echt commentaar; `web/index.html` en `flutter_bootstrap.js` bevatten er geen. Kandidaat voor `zap/baseline.conf` op IGNORE. - **10015 "Re-examine Cache-control"** — Apache-standaard op de HTML; adviserend, geen lek. **Reproduceren** ``` make dast DAST_URL=https://ocideck.librekat.nl/ ``` Scanner: OWASP ZAP stable (baseline, passief) via `make dast`. Uitgevoerd 2026-07-25.
Author
Owner

Opgepakt. Tak: fix/849-web-security-headers. Reikwijdte: web/.htaccess (nieuw, mod_headers), docs/HOSTING.md + docs/BUILD.md, zap/baseline.conf, plus een poort die bewaakt dat de headerset niet stil kan verdwijnen. Bewuste conservatieve keuze op de sticky headers: HSTS met max-age=1j + includeSubDomains maar zonder preload, en COEP (90004) laat ik weg — die kan cross-origin resources breken en vraagt eerst een test. 10027 gaat op IGNORE (vals-positief uit main.dart.js).

Opgepakt. Tak: fix/849-web-security-headers. Reikwijdte: web/.htaccess (nieuw, mod_headers), docs/HOSTING.md + docs/BUILD.md, zap/baseline.conf, plus een poort die bewaakt dat de headerset niet stil kan verdwijnen. Bewuste conservatieve keuze op de sticky headers: HSTS met max-age=1j + includeSubDomains maar zonder preload, en COEP (90004) laat ik weg — die kan cross-origin resources breken en vraagt eerst een test. 10027 gaat op IGNORE (vals-positief uit main.dart.js).
Author
Owner

Gebouwd en gemerged: commit 67d9fb02 (PR #857), op main geverifieerd — web/.htaccess staat er. Bevat de header-CSP (spiegelt de meta-CSP, bewaakt door test/web_htaccess_headers_test.dart onder make check en check_web_hardening.dart op de gebouwde bundel), X-Frame-Options DENY, nosniff, Referrer-Policy no-referrer, Permissions-Policy en HSTS (includeSubDomains, geen preload). ZAP-baseline: 10027 op IGNORE. make check + check-secrets + sast groen.

Bewust NIET meegenomen: COEP/require-corp (kan cross-origin resources breken, vraagt eerst een test) en de eigenlijke uitrol naar de live host — die deploy en de post-deploy-DAST-validatie (#850) tegen de echte Apache-host horen bij jou. Voorwaarde daar: mod_headers aan en AllowOverride die FileInfo/All toestaat; anders horen dezelfde regels in de vhost (docs/HOSTING.md §3).

Gebouwd en gemerged: commit 67d9fb02 (PR #857), op main geverifieerd — web/.htaccess staat er. Bevat de header-CSP (spiegelt de meta-CSP, bewaakt door test/web_htaccess_headers_test.dart onder make check en check_web_hardening.dart op de gebouwde bundel), X-Frame-Options DENY, nosniff, Referrer-Policy no-referrer, Permissions-Policy en HSTS (includeSubDomains, geen preload). ZAP-baseline: 10027 op IGNORE. make check + check-secrets + sast groen. Bewust NIET meegenomen: COEP/require-corp (kan cross-origin resources breken, vraagt eerst een test) en de eigenlijke uitrol naar de live host — die deploy en de post-deploy-DAST-validatie (#850) tegen de echte Apache-host horen bij jou. Voorwaarde daar: mod_headers aan en AllowOverride die FileInfo/All toestaat; anders horen dezelfde regels in de vhost (docs/HOSTING.md §3).
Author
Owner

Live uitgerold. De repo-fix (#857) stond op main maar de host stuurde de headers nog niet; nu wel. Gedeployd vanuit een geïsoleerde checkout van main (v0.1.0-8-g8c440e0f) met make deploy-web — de gedeelde werkkopie stond op een andere tak, dus bewust niet van daaruit.

Voorwaarden op de host geverifieerd: mod_headers geladen, AllowOverride All op /var/www/ocideck (009-ocideck-le-ssl.conf). .htaccess wordt toegepast en is terecht niet downloadbaar (403).

curl https://ocideck.librekat.nl/ toont nu live: Content-Security-Policy (met afdwingbare frame-ancestors 'none'), X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy no-referrer, Permissions-Policy, en HSTS (max-age=63072000; includeSubDomains). Ook 404-pagina's dragen ze.

ZAP-baseline vóór→na tegen de live host: 9→6 WARN, PASS 56→58. Weg: 10020 (clickjacking) en 10021 (nosniff). 10027 nu op IGNORE. Rest is bewust/adviserend: 90004 (COEP, bewust weggelaten — vraagt cross-origin test), 10055 (meta-CSP houdt frame-ancestors, nu door de header afgedwongen; cosmetisch), 10015 (Apache-cachestandaard). De resterende x1-tellingen op 10035/10038/10063 zijn een scan-artefact: curl bevestigt dat / én 404's de headers dragen.

Live uitgerold. De repo-fix (#857) stond op main maar de host stuurde de headers nog niet; nu wel. Gedeployd vanuit een geïsoleerde checkout van main (v0.1.0-8-g8c440e0f) met make deploy-web — de gedeelde werkkopie stond op een andere tak, dus bewust niet van daaruit. Voorwaarden op de host geverifieerd: mod_headers geladen, AllowOverride All op /var/www/ocideck (009-ocideck-le-ssl.conf). .htaccess wordt toegepast en is terecht niet downloadbaar (403). curl https://ocideck.librekat.nl/ toont nu live: Content-Security-Policy (met afdwingbare frame-ancestors 'none'), X-Frame-Options DENY, X-Content-Type-Options nosniff, Referrer-Policy no-referrer, Permissions-Policy, en HSTS (max-age=63072000; includeSubDomains). Ook 404-pagina's dragen ze. ZAP-baseline vóór→na tegen de live host: 9→6 WARN, PASS 56→58. Weg: 10020 (clickjacking) en 10021 (nosniff). 10027 nu op IGNORE. Rest is bewust/adviserend: 90004 (COEP, bewust weggelaten — vraagt cross-origin test), 10055 (meta-CSP houdt frame-ancestors, nu door de header afgedwongen; cosmetisch), 10015 (Apache-cachestandaard). De resterende x1-tellingen op 10035/10038/10063 zijn een scan-artefact: curl bevestigt dat / én 404's de headers dragen.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
LibreKAT/Ocideck#849
No description provided.