Without Permissions-Policy, any JavaScript executing on your page — including third-party analytics scripts, embedded widgets, and compromised CDN assets — can request access to the user's camera, microphone, and geolocation without additional browser prompts. If an XSS payload fires or a third-party script is compromised, it can silently activate hardware sensors. CWE-732 (Incorrect Permission Assignment) and OWASP A05 (Security Misconfiguration) both apply to unrestricted browser API exposure. Restricting permissions you don't use follows the principle of least privilege: a payment app has no legitimate reason to access the gyroscope.
Low because exploiting unrestricted permissions requires additional code execution context, but the policy is trivial to add and eliminates unnecessary browser API surface.
Add a Permissions-Policy header that explicitly denies browser APIs your app doesn't use. Deny everything you don't need — you can always add specific origins later.
// next.config.js
headers: [{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=(), payment=(), usb=()'
}]
The () syntax denies the feature for all origins, including your own pages. Use (self) to allow the feature only for your own origin. If you do need camera access (e.g., for a video feature), set camera=(self) instead of allowing it globally.
ID: security-headers.headers.permissions-policy
Severity: low
What to look for: Count all header configuration locations (framework config, middleware, deployment config) and check for a Permissions-Policy (or older Feature-Policy) header. Enumerate which sensitive browser APIs are restricted: camera, microphone, geolocation, etc.
Pass criteria: A Permissions-Policy (or legacy Feature-Policy) header is present that restricts at least 3 of the following sensitive APIs: camera, microphone, geolocation, payment, usb, magnetometer, gyroscope, accelerometer. Each restricted API must be set to () (deny all) or (self) (same-origin only), not left unrestricted.
Fail criteria: No Permissions-Policy header configured, or header is present but restricts fewer than 3 sensitive APIs.
Skip (N/A) when: Never.
Detail on fail: "No Permissions-Policy header configured — all browser APIs available to embedded content by default"
Remediation: Permissions-Policy lets you control which browser features your site and embedded content can use:
headers: [{
key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=()'
}]
The () syntax means "disallow for all origins". Add specific origins if your app needs these features: camera=(self).