|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152 |
- # Permissions-Policy reporting (Apache)
-
- The live `Permissions-Policy` header denies features with an empty allowlist
- (`feature=()` — grant to nobody). That header does **not** send reports by
- itself.
-
- Unlike CSP, Permissions-Policy has **no `report-uri`**. Reporting uses the
- Reporting API: a named endpoint in `Reporting-Endpoints`, plus a per-feature
- `report-to` parameter.
-
- Reuse the existing CSP collector:
-
- - UAT: `https://pnspsuat.gld.gov.hk/api/csp-report`
- - PROD: `https://pnsps.gld.gov.hk/api/csp-report`
-
- Backend: `CspReportController` (`POST /csp-report`) already logs the raw JSON
- and `Content-Type` and returns `204`. No backend change is required.
-
- See also: [csp-apache.conf.md](./csp-apache.conf.md)
-
- ## Why not `report-uri`
-
- This CSP-style trailer does **not** produce Permissions-Policy reports:
-
- ```apache
- # Wrong — browsers ignore report-uri on Permissions-Policy
- Header always set Permissions-Policy "camera=(), geolocation=(); report-uri https://pnspsuat.gld.gov.hk/api/csp-report"
- ```
-
- `report-to` is a **parameter of each feature**, not a global trailing directive.
-
- ## Does every feature need `report-to`?
-
- Yes, **if you want a report for that feature**.
-
- - `feature=()` — still blocked, but silent (no report)
- - `feature=();report-to=csp-endpoint` — blocked **and** reported
-
- There is no `all=()` reporter and no header-level `report-to` that applies to
- every feature. Add `;report-to=csp-endpoint` only on features you want in the
- log. Leave the rest as `()` if you do not need those reports.
-
- ```apache
- # camera is reported; geolocation is still blocked, but not reported
- Header always set Permissions-Policy "camera=();report-to=csp-endpoint, geolocation=()"
- ```
-
- ## Enforcing + reporting
-
- Keep the current deny-all policy. Add `Reporting-Endpoints`, then append
- `;report-to=csp-endpoint` on each feature you want to monitor.
-
- Copy the feature list from the live header; only the `report-to` parameter is
- new. Example (UAT):
-
- ```apache
- Header always set Reporting-Endpoints "csp-endpoint=\"https://pnspsuat.gld.gov.hk/api/csp-report\""
-
- Header always set Permissions-Policy "\
- accelerometer=();report-to=csp-endpoint, \
- autoplay=();report-to=csp-endpoint, \
- camera=();report-to=csp-endpoint, \
- display-capture=();report-to=csp-endpoint, \
- encrypted-media=();report-to=csp-endpoint, \
- fullscreen=();report-to=csp-endpoint, \
- geolocation=();report-to=csp-endpoint, \
- gyroscope=();report-to=csp-endpoint, \
- magnetometer=();report-to=csp-endpoint, \
- microphone=();report-to=csp-endpoint, \
- midi=();report-to=csp-endpoint, \
- payment=();report-to=csp-endpoint, \
- picture-in-picture=();report-to=csp-endpoint, \
- publickey-credentials-get=();report-to=csp-endpoint, \
- screen-wake-lock=();report-to=csp-endpoint, \
- usb=();report-to=csp-endpoint, \
- web-share=();report-to=csp-endpoint, \
- xr-spatial-tracking=();report-to=csp-endpoint"
- ```
-
- PROD: same headers, with
-
- `https://pnsps.gld.gov.hk/api/csp-report`
-
- Because deny-all is already enforced, put `report-to` on **`Permissions-Policy`**
- (not only Report-Only). Those reports have `disposition: "enforce"`.
-
- ## Report-Only (observe without blocking)
-
- Use `Permissions-Policy-Report-Only` only when testing a restriction that is
- **not** already denied by the enforcing header. Reports have
- `disposition: "report"`.
-
- ```apache
- Header always set Reporting-Endpoints "csp-endpoint=\"https://pnspsuat.gld.gov.hk/api/csp-report\""
- Header always set Permissions-Policy-Report-Only "geolocation=();report-to=csp-endpoint"
- ```
-
- Report-Only cannot re-enable a feature already denied by `Permissions-Policy`.
-
- ## Same URL, different payload
-
- Keep CSP on `report-uri` as it is. The collector URL is shared; the body is not.
-
- | Source | `Content-Type` | Body |
- | --- | --- | --- |
- | CSP `report-uri` | `application/csp-report` | `{ "csp-report": { … } }` |
- | Permissions-Policy | `application/reports+json` | JSON **array**, `type` = `permissions-policy-violation` |
-
- Example Permissions-Policy report:
-
- ```json
- [{
- "type": "permissions-policy-violation",
- "url": "https://pnspsuat.gld.gov.hk/",
- "body": {
- "disposition": "enforce",
- "featureId": "geolocation",
- "message": "Permissions policy violation: geolocation access has been blocked because of a permissions policy applied to the current document."
- }
- }]
- ```
-
- Chrome often serializes the feature as `policyId` instead of `featureId`. Filter
- logs on `permissions-policy-violation` vs `csp-report` so the two streams stay
- distinct.
-
- Optional: also point CSP at the same named endpoint (CSP `report-uri` remains
- for older browsers):
-
- ```apache
- Header always set Content-Security-Policy-Report-Only "…; report-uri https://pnspsuat.gld.gov.hk/api/csp-report; report-to csp-endpoint"
- ```
-
- ## Follow-up checklist
-
- - [ ] Confirm the live Apache `Permissions-Policy` feature list (deny-all `()`).
- - [ ] Add `Reporting-Endpoints` → existing `/api/csp-report` (UAT vs PROD host).
- - [ ] Add `;report-to=csp-endpoint` only on features you want in the log.
- - [ ] Deploy to UAT first; trigger a blocked API (e.g. `navigator.geolocation`) in Chrome/Edge.
- - [ ] Confirm a `permissions-policy-violation` line in the backend log (may be batched, a few seconds later).
- - [ ] Repeat on PROD with the PROD report URL.
-
- ## Notes
-
- - Reporting is Chromium-only (Chrome / Edge). Safari and Firefox still enforce
- `()` and usually send nothing.
- - Reports are batched and may arrive a few seconds after the violation, not on
- the same page request.
- - Same-origin `/api/csp-report` needs no extra CORS setup. The endpoint is
- already unauthenticated and CSRF is disabled.
- - Do not allowlist a feature in `Permissions-Policy` just to silence a report.
- Only grant a feature if the application itself needs it.
|