|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212 |
- # CSP Report Review (PROD + UAT, 2026-08-14)
-
- Review of Content-Security-Policy-Report-Only violations from PNSPS PROD
- (`https://pnsps.gld.gov.hk`) and UAT (`https://pnspsuat.gld.gov.hk`), for
- deciding Apache CSP updates.
-
- Source logs (external):
-
- - `P1 CSP.txt` — PROD node, 168 reports, 2026-07-30 to 2026-08-14
- - `P2 CSP.txt` — PROD node, 137 reports, 2026-07-30 to 2026-08-14
- - `TT CSP.txt` — UAT, 133 reports, 2026-05-29 to 2026-08-03
-
- Related app docs:
-
- - [csp-apache.conf.md](./csp-apache.conf.md) — Apache header snippets to deploy
- - [csp-report-review-2026-08-03.md](./csp-report-review-2026-08-03.md) — earlier PROD review
-
- ---
-
- ## Verdict
-
- CSP is still **Report-Only** (`disposition: report`) — nothing is blocking users
- yet. There is **one real PNSPS SPA problem** (proof PDF preview `frame-src`).
- Everything else is browser-extension noise or a **different app** on the same
- UAT host.
-
- The 2026-08-03 recommendation to add `frame-src` is **still not on the live
- header**.
-
- ---
-
- ## Add / remove summary
-
- **PROD — add one line, remove nothing:**
-
- ```apache
- frame-src 'self' data: blob:;
- ```
-
- Keep `script-src 'self'`. Do not add `'unsafe-inline'` or `'unsafe-eval'`.
-
- **UAT — add the same line, remove nothing now:**
-
- ```apache
- frame-src 'self' data: blob:;
- ```
-
- Keep `script-src 'self' 'unsafe-inline' 'unsafe-eval'` for now (JSF payment recon
- on the same host). Do not copy this `script-src` to PROD.
-
- **Do not add on either env:** `'wasm-unsafe-eval'`, Youdao, NetEase CDN,
- `todesktop-internal`, Google Fonts.
-
- **Optional later (not required for these reports):** UAT can drop
- `'unsafe-inline' 'unsafe-eval'` from the SPA header only after
- `/paymentrecon-dept_web/` has its own CSP.
-
- ---
-
- ## Confirmed current PROD policy (Report-Only)
-
- This is the live header that generated the P1/P2 reports. It matches the
- `original-policy` in every PROD violation.
-
- ```apache
- Header always set Content-Security-Policy-Report-Only "default-src 'self'; \
- base-uri 'self'; \
- object-src 'none'; \
- frame-ancestors 'none'; \
- form-action 'self'; \
- script-src 'self'; \
- style-src 'self' 'unsafe-inline'; \
- style-src-elem 'self' 'unsafe-inline'; \
- img-src 'self' data: https://www.w3.org https://w3.org; \
- media-src 'self' blob:; \
- font-src 'self' data:; \
- connect-src 'self'; \
- upgrade-insecure-requests; \
- report-uri https://pnsps.gld.gov.hk/api/csp-report"
- ```
-
- Gap vs the reports: **no `frame-src`**. Framing therefore falls back to
- `default-src 'self'`, which is why proof PDF preview reports `frame-src` with
- an empty `blocked-uri`.
-
- ---
-
- ## Confirmed current UAT policy (Report-Only)
-
- ```apache
- Header always set Content-Security-Policy-Report-Only "default-src 'self'; \
- base-uri 'self'; \
- object-src 'none'; \
- frame-ancestors 'none'; \
- form-action 'self'; \
- script-src 'self' 'unsafe-inline' 'unsafe-eval'; \
- style-src 'self' 'unsafe-inline'; \
- style-src-elem 'self' 'unsafe-inline'; \
- img-src 'self' data: https://www.w3.org https://w3.org; \
- media-src 'self' blob:; \
- font-src 'self' data:; \
- connect-src 'self'; \
- upgrade-insecure-requests; \
- report-uri https://pnspsuat.gld.gov.hk/api/csp-report"
- ```
-
- UAT vs PROD differences:
-
- - UAT `script-src` is `'self' 'unsafe-inline' 'unsafe-eval'` — PROD is `'self'` only.
- - Both still lack `frame-src`.
- - TT report `original-policy` was older: `script-src 'self' 'unsafe-inline'`
- **without** `'unsafe-eval'`. That is why TT logged 42 JSF `eval` hits. Those
- should stop under the current UAT header.
-
- Do **not** copy `'unsafe-inline'` or `'unsafe-eval'` onto PROD. The React SPA
- does not need them. `'unsafe-eval'` is only there for the shared-host JSF
- payment recon app.
-
- ---
-
- ## Real problem (PROD P1 + P2)
-
- | Volume | Directive | Blocked | Pages |
- |---:|---|---|---|
- | 150 | `frame-src` | empty `""` | `/proof/create/{id}` |
-
- Source is always `static/js/4608.4af455e6.chunk.js`. Cause is proof file preview
- in `src/pages/Proof/Create_FromApp/UploadFileTable.js`:
- `FileReader.readAsDataURL` then `document.write` an `<iframe src="data:...">`.
- With no `frame-src`, CSP uses `default-src 'self'` and blocks `data:` frames.
- Browsers strip the `data:` URI from reports (empty `blocked-uri`).
-
- **If this policy is promoted to enforcing without `frame-src`, PDF preview on
- proof create will break.**
-
- Apache fix (already documented in [csp-apache.conf.md](./csp-apache.conf.md),
- not deployed):
-
- ```apache
- frame-src 'self' data: blob:;
- ```
-
- Optional frontend follow-up: switch preview to `URL.createObjectURL` + `blob:`
- (still needs `frame-src ... blob:`).
-
- Optional (low priority): `media-src 'self' blob: data:;` — current `blob:`
- already covers captcha audio.
-
- ---
-
- ## Not a PNSPS SPA problem — do not allowlist
-
- | Volume | Where | Directive | Blocked | Why ignore |
- |---:|---|---|---|---|
- | 109 | PROD | `script-src` | `wasm-eval` | `source-file: chrome-extension` |
- | 18 | PROD | `media-src` | `dict.youdao.com` | Youdao translation plugin |
- | 11 | PROD | `img-src` | `ydlunacommon-cdn.nosdn.127.net` | Youdao/NetEase CDN icons |
- | 3 | PROD P2 | `script-src-elem` | `todesktop-internal` | Desktop wrapper |
- | 105 | PROD 14 + UAT 91 | `img-src` | `www.w3.org/WAI/wcag2AA` | Already allowed in policy; mostly SPA `status-code: 404` noise. Badge is in `src/components/cards/AuthFooter.js`. |
-
- Do **not** add `'unsafe-eval'`, `'wasm-unsafe-eval'`, Youdao, NetEase, or
- `todesktop-internal` to the SPA policy.
-
- ---
-
- ## UAT-only: payment recon JSF (not the React app)
-
- 42 TT reports: `script-src` / `eval` on:
-
- - `/paymentrecon-dept_web/report/paymentReconRpt01Search.jsf` (38)
- - `/paymentrecon-dept_web/report/paymentReconRpt02Search.jsf` (3)
- - `/paymentrecon-dept_web/manualrecon/manualReconSearch.jsf` (1)
-
- This is a **separate JSF app** on the same Apache host. The TT logs were
- generated under the **old** UAT policy (`script-src 'self' 'unsafe-inline'`).
- Current UAT already adds `'unsafe-eval'`, so those `eval` reports should
- disappear.
-
- That UAT looseness is a workaround for JSF, not a requirement of PNSPS. Before
- enforcing CSP:
-
- - Keep PROD at `script-src 'self'` (do not add `'unsafe-eval'`).
- - Prefer a path-specific header for `/paymentrecon-dept_web/` on UAT so the SPA
- can later drop `'unsafe-inline'` / `'unsafe-eval'`.
-
- ---
-
- ## Recommended actions (ops / Apache, not app code)
-
- 1. Add `frame-src 'self' data: blob:;` to both PROD and UAT Report-Only headers.
- Do not change PROD `script-src 'self'`.
- 2. Confirm proof-create preview reports drop after that deploy.
- 3. Keep Report-Only until `frame-src` volume is gone; remaining extension noise
- is expected.
- 4. Do not promote UAT's `'unsafe-inline' 'unsafe-eval'` to PROD. Those tokens
- exist for JSF payment recon on the shared UAT host.
- 5. Optional: give `/paymentrecon-dept_web/` its own Apache CSP so UAT SPA can
- later match PROD (`script-src 'self'`).
- 6. Optional later: host the WCAG badge locally to cut `img-src` noise; change
- preview to `blob:` URLs.
-
- No application code change is required for the reports to become clean after
- the Apache `frame-src` deploy.
-
- ---
-
- ## Backend reference
-
- - Report endpoint: `POST /csp-report` (context path → `/api/csp-report`)
- - Spring Security also sets a short API CSP on API responses
- (`default-src 'self'; script-src 'self'; frame-ancestors 'self'`).
- Frontend/Apache CSP above is what browsers enforce for the SPA document.
|