Jason Chuang 1 день тому
джерело
коміт
06acc53c99
2 змінених файлів з 178 додано та 5 видалено
  1. +12
    -5
      docs/csp-apache.conf.md
  2. +166
    -0
      docs/csp-report-review-2026-08-03.md

+ 12
- 5
docs/csp-apache.conf.md Переглянути файл

@@ -2,7 +2,9 @@

Self-hosted fonts: no `fonts.googleapis.com` or `fonts.gstatic.com`. Static assets (including `woff2`) are served from `'self'`.

Adjust `report-uri` if your API base path or host differs (example below targets UAT).
Adjust `report-uri` if your API base path or host differs (UAT example below; PROD uses `https://pnsps.gld.gov.hk/api/csp-report`).

See also: [csp-report-review-2026-08-03.md](./csp-report-review-2026-08-03.md) for the PROD report analysis that drove `frame-src` / `media-src` updates.

## Enforcing

@@ -12,11 +14,12 @@ Header always set Content-Security-Policy "default-src 'self'; \
object-src 'none'; \
frame-ancestors 'none'; \
form-action 'self'; \
frame-src 'self' data: blob:; \
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:; \
media-src 'self' blob: data:; \
font-src 'self' data:; \
connect-src 'self'; \
upgrade-insecure-requests"
@@ -32,11 +35,12 @@ Header always set Content-Security-Policy-Report-Only "default-src 'self'; \
object-src 'none'; \
frame-ancestors 'none'; \
form-action 'self'; \
frame-src 'self' data: blob:; \
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:; \
media-src 'self' blob: data:; \
font-src 'self' data:; \
connect-src 'self'; \
upgrade-insecure-requests; \
@@ -45,7 +49,10 @@ Header always set Content-Security-Policy-Report-Only "default-src 'self'; \

## Notes

- **`frame-src`**: Required for proof file preview iframes that use `data:` / `blob:` URLs (`UploadFileTable.js`). Without this, framing falls back to `default-src 'self'` and browsers report `frame-src` violations (often with empty `blocked-uri`).
- **`style-src-elem`**: Explicit, alongside `style-src`, for `<link rel="stylesheet">` behaviour in modern browsers.
- **`img-src`**: Includes `https://www.w3.org` and `https://w3.org` so W3C URLs that redirect between hosts are allowed.
- **`img-src`**: Includes `https://www.w3.org` and `https://w3.org` so W3C WCAG badge URLs are allowed.
- **`media-src`**: `blob:` for captcha audio object URLs; `data:` for data-URI media if used.
- **`font-src`**: `'self' data:` covers bundled fonts and `data:` URLs if used.
- Add origins to the relevant directive only if you introduce third-party scripts, styles, fonts, or APIs.
- **Report noise**: Browser extensions (Kaspersky, Perplexity, Youdao, Quark, Google Fonts injected by tooling, `wasm-eval` from chrome-extension) will still appear under Report-Only. Do **not** allowlist those origins.
- Add origins to the relevant directive only if you introduce third-party scripts, styles, fonts, or APIs that the **application** itself loads.

+ 166
- 0
docs/csp-report-review-2026-08-03.md Переглянути файл

@@ -0,0 +1,166 @@
# CSP Report Review (PROD, 2026-08-03)

Review of Content-Security-Policy-Report-Only violations from PNSPS PROD
(`https://pnsps.gld.gov.hk`), for deciding Apache CSP updates.

Source logs (external):

- `PNSPS PROD CSP Report 2026-08-03.txt`
- `summary.txt`
- `CSP Issue 1.txt` (`script-src` / `eval` samples)

Related app docs:

- [csp-apache.conf.md](./csp-apache.conf.md) — Apache header snippets to deploy

---

## Policy in effect at report time

```
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
```

Notes:

- Disposition was `report` (Report-Only); nothing was enforced.
- No `frame-src` → framing falls back to `default-src 'self'`.

---

## Summary of findings

Most reports are **browser / extension noise**. Only one directive change is
required for **app functionality**: add `frame-src`.

| Approx. volume | Directive | Blocked | Verdict |
|---:|---|---|---|
| ~3000 | `frame-src` | empty / `data:` (URI often stripped) | **App** — PDF preview iframes |
| ~1500 | `script-src` | `wasm-eval` | Noise — `chrome-extension` |
| ~1400 | `font-src` | `fonts.gstatic.com` (Inter / Nunito) | Noise — not app fonts |
| ~500 | `img-src` | `https://www.w3.org/WAI/wcag2AA` | Already allowed; mostly doc `404` noise |
| ~240 | `font-src` | `at.alicdn.com`, `cdn.yiban.io`, Perplexity CDN | Noise — toolbars / extensions |
| ~180 | `style-src-elem` | `fonts.googleapis.com` | Noise — not loaded by app |
| ~70 | `script-src` | `eval` (line ~67) | Noise — no app `eval` |
| rest | various | Kaspersky, Youdao, Quark, `todesktop-internal`, etc. | Noise |

App fonts are self-hosted (`@fontsource` Public Sans / Noto; see `public/index.html`
and `src/assets/fonts.css`). Google Fonts (Roboto / Inter / Nunito) are **not**
part of the app and must not be allowlisted.

---

## App-owned issue: `frame-src`

### Cause

Proof upload preview in
`src/pages/Proof/Create_FromApp/UploadFileTable.js`:

1. `FileReader.readAsDataURL(...)` builds a `data:` URL
2. `window.open("")` then `document.write` an `<iframe src="data:...">`

With no `frame-src`, CSP uses `default-src 'self'`, which blocks `data:` frames.
Browsers often report `blocked-uri` as empty (`""`) for privacy.

Almost all of these hits are on `/proof/create/...`, source
`static/js/4608.*.chunk.js`.

### Apache fix

```apache
frame-src 'self' data: blob:;
```

### Optional follow-up (frontend)

Prefer `URL.createObjectURL` + `blob:` for preview (still needs `frame-src ... blob:`).

---

## Optional: `media-src` and `data:`

Small volume of `media-src` / `data` on login and related pages.

Captcha audio already uses `blob:` (`CustomFormWizard.js` and similar). Adding
`data:` is low risk:

```apache
media-src 'self' blob: data:;
```

---

## Already covered / not a CSP gap

### WCAG badge (`img-src` / `www.w3.org`)

`AuthFooter.js` loads `https://www.w3.org/WAI/wcag2AA`. Policy already allows
`https://www.w3.org` and `https://w3.org`. Many reports had document
`status-code: 404` (SPA shell). No Apache change required.

Optional hardening: host the badge image locally.

### Payment gateway CSS (`epaygateway1.gcis.gov.hk`)

Single `style-src-elem` hit on `/paymentPage/callback` with gateway referrer.
Card payment uses `window.location.assign(redirecturl)`, not embedded gateway CSS.
Do **not** allowlist unless a real UI break is confirmed.

---

## Do not add to CSP

| Requested by reports | Why not |
|---|---|
| `'unsafe-eval'` / `'wasm-unsafe-eval'` | Extension / injected; app has no `eval` |
| `fonts.googleapis.com` / `fonts.gstatic.com` | App uses self-hosted fonts |
| Kaspersky (`*.kis.v2.scr.kaspersky-labs.com`) | AV injection |
| Perplexity, Youdao, Quark, alicdn, yiban, NetEase CDN | Third-party client tooling |
| `todesktop-internal` | Desktop wrapper noise |
| `epaygateway1.gcis.gov.hk` (styles) | Not required for current payment flow |

---

## Recommended Apache policy

Use the snippets in [csp-apache.conf.md](./csp-apache.conf.md).

Changes vs the 2026-08-03 deployed policy:

1. Add `frame-src 'self' data: blob:;`
2. Change `media-src` to `'self' blob: data:;`

### Rollout

1. Deploy updated **Report-Only** header.
2. Watch `/api/csp-report` for a few days — expect `frame-src` volume to drop;
remaining extension noise is OK.
3. Promote the same policy to **enforcing** when app-owned directives look clean.

Adjust `report-uri` host for UAT vs PROD:

- UAT: `https://pnspsuat.gld.gov.hk/api/csp-report`
- PROD: `https://pnsps.gld.gov.hk/api/csp-report`

---

## 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.

Завантаження…
Відмінити
Зберегти