You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

csp-report-review-2026-08-14.md 7.2 KiB

1 kuukausi sitten
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212
  1. # CSP Report Review (PROD + UAT, 2026-08-14)
  2. Review of Content-Security-Policy-Report-Only violations from PNSPS PROD
  3. (`https://pnsps.gld.gov.hk`) and UAT (`https://pnspsuat.gld.gov.hk`), for
  4. deciding Apache CSP updates.
  5. Source logs (external):
  6. - `P1 CSP.txt` — PROD node, 168 reports, 2026-07-30 to 2026-08-14
  7. - `P2 CSP.txt` — PROD node, 137 reports, 2026-07-30 to 2026-08-14
  8. - `TT CSP.txt` — UAT, 133 reports, 2026-05-29 to 2026-08-03
  9. Related app docs:
  10. - [csp-apache.conf.md](./csp-apache.conf.md) — Apache header snippets to deploy
  11. - [csp-report-review-2026-08-03.md](./csp-report-review-2026-08-03.md) — earlier PROD review
  12. ---
  13. ## Verdict
  14. CSP is still **Report-Only** (`disposition: report`) — nothing is blocking users
  15. yet. There is **one real PNSPS SPA problem** (proof PDF preview `frame-src`).
  16. Everything else is browser-extension noise or a **different app** on the same
  17. UAT host.
  18. The 2026-08-03 recommendation to add `frame-src` is **still not on the live
  19. header**.
  20. ---
  21. ## Add / remove summary
  22. **PROD — add one line, remove nothing:**
  23. ```apache
  24. frame-src 'self' data: blob:;
  25. ```
  26. Keep `script-src 'self'`. Do not add `'unsafe-inline'` or `'unsafe-eval'`.
  27. **UAT — add the same line, remove nothing now:**
  28. ```apache
  29. frame-src 'self' data: blob:;
  30. ```
  31. Keep `script-src 'self' 'unsafe-inline' 'unsafe-eval'` for now (JSF payment recon
  32. on the same host). Do not copy this `script-src` to PROD.
  33. **Do not add on either env:** `'wasm-unsafe-eval'`, Youdao, NetEase CDN,
  34. `todesktop-internal`, Google Fonts.
  35. **Optional later (not required for these reports):** UAT can drop
  36. `'unsafe-inline' 'unsafe-eval'` from the SPA header only after
  37. `/paymentrecon-dept_web/` has its own CSP.
  38. ---
  39. ## Confirmed current PROD policy (Report-Only)
  40. This is the live header that generated the P1/P2 reports. It matches the
  41. `original-policy` in every PROD violation.
  42. ```apache
  43. Header always set Content-Security-Policy-Report-Only "default-src 'self'; \
  44. base-uri 'self'; \
  45. object-src 'none'; \
  46. frame-ancestors 'none'; \
  47. form-action 'self'; \
  48. script-src 'self'; \
  49. style-src 'self' 'unsafe-inline'; \
  50. style-src-elem 'self' 'unsafe-inline'; \
  51. img-src 'self' data: https://www.w3.org https://w3.org; \
  52. media-src 'self' blob:; \
  53. font-src 'self' data:; \
  54. connect-src 'self'; \
  55. upgrade-insecure-requests; \
  56. report-uri https://pnsps.gld.gov.hk/api/csp-report"
  57. ```
  58. Gap vs the reports: **no `frame-src`**. Framing therefore falls back to
  59. `default-src 'self'`, which is why proof PDF preview reports `frame-src` with
  60. an empty `blocked-uri`.
  61. ---
  62. ## Confirmed current UAT policy (Report-Only)
  63. ```apache
  64. Header always set Content-Security-Policy-Report-Only "default-src 'self'; \
  65. base-uri 'self'; \
  66. object-src 'none'; \
  67. frame-ancestors 'none'; \
  68. form-action 'self'; \
  69. script-src 'self' 'unsafe-inline' 'unsafe-eval'; \
  70. style-src 'self' 'unsafe-inline'; \
  71. style-src-elem 'self' 'unsafe-inline'; \
  72. img-src 'self' data: https://www.w3.org https://w3.org; \
  73. media-src 'self' blob:; \
  74. font-src 'self' data:; \
  75. connect-src 'self'; \
  76. upgrade-insecure-requests; \
  77. report-uri https://pnspsuat.gld.gov.hk/api/csp-report"
  78. ```
  79. UAT vs PROD differences:
  80. - UAT `script-src` is `'self' 'unsafe-inline' 'unsafe-eval'` — PROD is `'self'` only.
  81. - Both still lack `frame-src`.
  82. - TT report `original-policy` was older: `script-src 'self' 'unsafe-inline'`
  83. **without** `'unsafe-eval'`. That is why TT logged 42 JSF `eval` hits. Those
  84. should stop under the current UAT header.
  85. Do **not** copy `'unsafe-inline'` or `'unsafe-eval'` onto PROD. The React SPA
  86. does not need them. `'unsafe-eval'` is only there for the shared-host JSF
  87. payment recon app.
  88. ---
  89. ## Real problem (PROD P1 + P2)
  90. | Volume | Directive | Blocked | Pages |
  91. |---:|---|---|---|
  92. | 150 | `frame-src` | empty `""` | `/proof/create/{id}` |
  93. Source is always `static/js/4608.4af455e6.chunk.js`. Cause is proof file preview
  94. in `src/pages/Proof/Create_FromApp/UploadFileTable.js`:
  95. `FileReader.readAsDataURL` then `document.write` an `<iframe src="data:...">`.
  96. With no `frame-src`, CSP uses `default-src 'self'` and blocks `data:` frames.
  97. Browsers strip the `data:` URI from reports (empty `blocked-uri`).
  98. **If this policy is promoted to enforcing without `frame-src`, PDF preview on
  99. proof create will break.**
  100. Apache fix (already documented in [csp-apache.conf.md](./csp-apache.conf.md),
  101. not deployed):
  102. ```apache
  103. frame-src 'self' data: blob:;
  104. ```
  105. Optional frontend follow-up: switch preview to `URL.createObjectURL` + `blob:`
  106. (still needs `frame-src ... blob:`).
  107. Optional (low priority): `media-src 'self' blob: data:;` — current `blob:`
  108. already covers captcha audio.
  109. ---
  110. ## Not a PNSPS SPA problem — do not allowlist
  111. | Volume | Where | Directive | Blocked | Why ignore |
  112. |---:|---|---|---|---|
  113. | 109 | PROD | `script-src` | `wasm-eval` | `source-file: chrome-extension` |
  114. | 18 | PROD | `media-src` | `dict.youdao.com` | Youdao translation plugin |
  115. | 11 | PROD | `img-src` | `ydlunacommon-cdn.nosdn.127.net` | Youdao/NetEase CDN icons |
  116. | 3 | PROD P2 | `script-src-elem` | `todesktop-internal` | Desktop wrapper |
  117. | 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`. |
  118. Do **not** add `'unsafe-eval'`, `'wasm-unsafe-eval'`, Youdao, NetEase, or
  119. `todesktop-internal` to the SPA policy.
  120. ---
  121. ## UAT-only: payment recon JSF (not the React app)
  122. 42 TT reports: `script-src` / `eval` on:
  123. - `/paymentrecon-dept_web/report/paymentReconRpt01Search.jsf` (38)
  124. - `/paymentrecon-dept_web/report/paymentReconRpt02Search.jsf` (3)
  125. - `/paymentrecon-dept_web/manualrecon/manualReconSearch.jsf` (1)
  126. This is a **separate JSF app** on the same Apache host. The TT logs were
  127. generated under the **old** UAT policy (`script-src 'self' 'unsafe-inline'`).
  128. Current UAT already adds `'unsafe-eval'`, so those `eval` reports should
  129. disappear.
  130. That UAT looseness is a workaround for JSF, not a requirement of PNSPS. Before
  131. enforcing CSP:
  132. - Keep PROD at `script-src 'self'` (do not add `'unsafe-eval'`).
  133. - Prefer a path-specific header for `/paymentrecon-dept_web/` on UAT so the SPA
  134. can later drop `'unsafe-inline'` / `'unsafe-eval'`.
  135. ---
  136. ## Recommended actions (ops / Apache, not app code)
  137. 1. Add `frame-src 'self' data: blob:;` to both PROD and UAT Report-Only headers.
  138. Do not change PROD `script-src 'self'`.
  139. 2. Confirm proof-create preview reports drop after that deploy.
  140. 3. Keep Report-Only until `frame-src` volume is gone; remaining extension noise
  141. is expected.
  142. 4. Do not promote UAT's `'unsafe-inline' 'unsafe-eval'` to PROD. Those tokens
  143. exist for JSF payment recon on the shared UAT host.
  144. 5. Optional: give `/paymentrecon-dept_web/` its own Apache CSP so UAT SPA can
  145. later match PROD (`script-src 'self'`).
  146. 6. Optional later: host the WCAG badge locally to cut `img-src` noise; change
  147. preview to `blob:` URLs.
  148. No application code change is required for the reports to become clean after
  149. the Apache `frame-src` deploy.
  150. ---
  151. ## Backend reference
  152. - Report endpoint: `POST /csp-report` (context path → `/api/csp-report`)
  153. - Spring Security also sets a short API CSP on API responses
  154. (`default-src 'self'; script-src 'self'; frame-ancestors 'self'`).
  155. Frontend/Apache CSP above is what browsers enforce for the SPA document.