Permissions-Policy (früher Feature-Policy) ist ein Header, mit dem die App den Browser anweist, welche Web-APIs überhaupt benutzt werden dürfen — und ob eingebettete iframes diese APIs ebenfalls nutzen dürfen. Klassische Anwendungs-Fälle: Kamera/Mikrofon nur in der eigenen App, Geolocation gar nicht, Payment-Request-API nur in Same-Origin-Frames. Wichtige Härtungs-Schicht gegen XSS-Eskalation, Drittpartei-Tracking und Browser-Feature-Missbrauch.
Was die Policy macht
Ohne Permissions-Policy darf jede eingebettete Ressource (Skript, iframe) prinzipiell die Browser-API-Permissions deiner Seite nutzen — wenn der User zustimmt. Wenn ein XSS-Bug es in die Seite schafft oder ein Third-Party-Tag-Manager unkontrolliert agiert, kann das Skript:
- Den User um Kamera-Zugriff bitten.
- Geolocation abfragen.
- Payment-Request initiieren.
- USB-/Bluetooth-Device-Verbindung anfordern.
Mit Permissions-Policy kann die App sagen: „Diese API ist hier komplett aus. Niemand kann sie nutzen, auch wenn der User zustimmt."
Beispiel:
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=()Alle vier APIs sind komplett deaktiviert. Der Browser blockt Requests gar nicht erst — keine Popup-Anfrage, kein Permission-Dialog.
Syntax
Format: <feature>=<allowlist> mit Komma-getrennten Features.
Allowlist-Werte:
()— niemand darf (auch nicht eigene Origin).(self)— nur eigene Origin darf.(self "https://partner.example.com")— eigene plus genannte Origins.*— alle (alle eingebetteten iframes dürfen).
Beispiele:
# Kamera nur in eigener Origin, Mikrofon gar nicht, Geo nur für Partner
Permissions-Policy: camera=(self), microphone=(), geolocation=(self "https://maps.partner.com")
# Payment-Request nur self
Permissions-Policy: payment=(self)
# Alle modernen APIs explizit deaktivieren (Maximal-Restriktiv)
Permissions-Policy: accelerometer=(), ambient-light-sensor=(), autoplay=(), battery=(),
camera=(), display-capture=(), document-domain=(), encrypted-media=(),
execution-while-not-rendered=(), execution-while-out-of-viewport=(),
fullscreen=(self), geolocation=(), gyroscope=(), magnetometer=(),
microphone=(), midi=(), navigation-override=(), payment=(),
picture-in-picture=(), publickey-credentials-get=(self),
screen-wake-lock=(), sync-xhr=(), usb=(), web-share=(),
xr-spatial-tracking=()Wichtige Features
| Feature | Beschreibung | Empfehlung |
|---|---|---|
camera | getUserMedia für Video | () oder (self) |
microphone | getUserMedia für Audio | () oder (self) |
geolocation | navigator.geolocation | () außer wenn gebraucht |
payment | Payment Request API | (self) für Checkout-Seiten, sonst () |
usb | WebUSB | () außer Hardware-Apps |
bluetooth | Web Bluetooth | () außer Hardware-Apps |
autoplay | Auto-play für Video/Audio | (self) oder () |
fullscreen | requestFullscreen | (self) für Video-Apps |
display-capture | Screen-Sharing | () außer Konferenz-Apps |
encrypted-media | DRM (Widevine, etc.) | (self) für Streaming |
publickey-credentials-get | WebAuthn | (self) für Auth |
accelerometer, gyroscope | Motion-Sensors | () außer Game/AR |
document-domain | document.domain-Setting (deprecated) | () immer |
web-share | Web Share API | (self) |
clipboard-read, clipboard-write | Clipboard-Zugriff | (self) |
Default-Empfehlung für Standard-App ohne spezielle Hardware:
Permissions-Policy: camera=(), microphone=(), geolocation=(), payment=(self),
usb=(), bluetooth=(), accelerometer=(), gyroscope=(),
magnetometer=(), display-capture=()iframe-Permissions
Bei eingebetteten iframes greift zusätzlich das allow-Attribut. Es kann nur Features aktivieren, die die Parent-Policy erlaubt — niemals erweitern.
<!-- Parent-Header: Permissions-Policy: camera=(self "https://video.example.com") -->
<!-- Iframe darf Kamera nutzen, weil Parent-Policy es erlaubt UND iframe-Allow es setzt -->
<iframe src="https://video.example.com/call"
allow="camera; microphone"></iframe>
<!-- Iframe darf Kamera NICHT nutzen, weil iframe-Allow es nicht setzt -->
<iframe src="https://video.example.com/other"></iframe>
<!-- Iframe darf Kamera NICHT nutzen, weil Parent-Policy diese Origin nicht erlaubt -->
<iframe src="https://fremde.example/call"
allow="camera"></iframe>Wichtig: das iframe-allow-Attribut kann nur einschränken oder gleich-bleiben, niemals erweitern. Auch wenn ein iframe allow="camera" setzt, muss die Parent-Policy es ebenfalls erlauben.
Migration von Feature-Policy
Feature-Policy war der alte Name (2018–2020). Wurde umbenannt zu Permissions-Policy (2020). Syntax leicht anders:
# Alt (Feature-Policy)
Feature-Policy: camera 'self'; microphone 'none'
# Neu (Permissions-Policy)
Permissions-Policy: camera=(self), microphone=()Unterschiede:
- Trennzeichen: Feature-Policy benutzt
;zwischen Features, Permissions-Policy,. - Allowlist-Syntax: Feature-Policy quotiert (
'self','none'), Permissions-Policy nutzt Klammern ((self),()). 'none'wird zu()— leere Klammer-Liste.
Stand 2026: alle aktuellen Browser unterstützen Permissions-Policy. Feature-Policy ist deprecated, kann aber als Backup parallel gesetzt werden für sehr alte Browser (selten relevant).
Konfiguration
nginx:
add_header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(self), usb=()" always;Express (Helmet):
// Helmet hat noch keine direkte API für Permissions-Policy (Stand 2026)
// Header manuell setzen
app.use((req, res, next) => {
res.setHeader('Permissions-Policy',
'camera=(), microphone=(), geolocation=(), payment=(self), usb=()');
next();
});Caddy:
header Permissions-Policy "camera=(), microphone=(), geolocation=(), payment=(self), usb=()"Reporting
Permissions-Policy unterstützt die Reporting-API:
Reporting-Endpoints: pp-endpoint="https://reports.example.com/permissions"
Permissions-Policy: camera=(self), microphone=()
Permissions-Policy-Report-Only: camera=(), microphone=();report-to=pp-endpointWenn ein Skript versucht, getUserMedia für Kamera aufzurufen, sendet der Browser einen Violation-Report — auch im Report-Only-Modus.
Use-Case: strikte Policy mit Report-Only ausrollen, beobachten, ob unerwartete Aufrufe kommen, dann auf Enforce umschalten.
Browser-Support und Quirks
Stand 2026:
- Chrome, Edge, Brave: voller Support seit Chrome 88 (2021).
- Firefox: Support seit Firefox 99 (2022) für die meisten Features.
- Safari: voller Support seit Safari 16.4 (2023).
Vorsicht: nicht jedes Feature ist in jedem Browser implementiert. Permissions-Policy Features List hat die aktuelle Implementierungs-Matrix.
Quirk: Manche Features sind fetch-Metadata-relevant. sync-xhr=() deaktiviert synchrone XHRs — kann legacy Code brechen.
Quirk: document-domain=() deaktiviert das Setzen von document.domain. Bei manchen Legacy-Single-Sign-On-Setups problematisch.
Interessantes
Default ist Browser-spezifisch, nicht überall "aus"
Wer keinen Permissions-Policy-Header setzt, hat die Browser-Defaults — die sind nicht überall maximal restriktiv. Klassisch: Kamera/Mic in Top-Frame erlaubt (mit User-Prompt), in Cross-Origin-iframes meist nicht. Explizit setzen ist klarer als auf Defaults zu vertrauen.
Permissions-Policy schützt nicht vor lokalem JS
Wenn Same-Origin-JS auf der Seite die Kamera anfordert und Policy camera=(self) ist, wird der User-Prompt gezeigt. Die Policy regelt, wo der API-Zugriff überhaupt möglich ist, nicht ob der User zustimmen muss. Plus XSS-Schutz: bei XSS-Lücke in self-Origin könnte Angreifer-Code die Kamera anfordern — Permission-Schicht hilft nur, wenn Policy camera=() ist.
Iframe-Embeds mit Restriktion
Wer YouTube-Embeds erlaubt, gibt mit <iframe allow="encrypted-media; picture-in-picture"> die nötigen Features. Pro Embed-Type minimal halten — nur die wirklich benötigten Features durchschleusen.
Sandbox + Permissions-Policy als Defense-in-Depth
<iframe sandbox> ist die ältere Schicht, blockt JS, Forms, Popups. allow="..." mit Permissions-Policy ist die Feature-Schicht. Beide kombinierbar: maximal restriktives iframe = Sandbox plus leere Permissions.
Web-Apps für Hardware brauchen explizite Permissions
Apps für 3D-Drucker, Mikrocontroller, USB-Sensoren brauchen usb=(self). Bluetooth-Apps brauchen bluetooth=(self). Default-Block ist Standard, nur dort wo gebraucht aktivieren.
Permissions-Policy für Cookies (`Permissions-Policy: cookie-store=()`?)
Die Spec hat experimentelle Features auch für Cookies, Storage, etc. — Stand 2026 nicht produktiv. Browser-Verhalten zu Storage-Beschränkungen wird klassisch über andere Header gesteuert (CHIPS für Cookies, Storage-Access-API).
Lockdown-Mode (iOS) und Permissions-Policy
Apple's Lockdown-Mode deaktiviert viele Features auf Browser-Ebene (Just-In-Time-Compilation, WebGL, einige APIs). Permissions-Policy operiert pro Seite — Lockdown-Mode ist User-System-Wide. Beide Schichten ergänzen sich: System-Lockdown ist Defense für High-Risk-User, Policy ist Defense für jede App.
Weiterführende Ressourcen
Externe Quellen
- W3C — Permissions Policy
- MDN — Permissions-Policy
- Permissions-Policy Features List
- permissionspolicy.com — Generator
- Chrome Developers — Permissions Policy
Verwandte Artikel
- Secure-Headers-Übersicht
- CSP-Deployment
- COOP/COEP/CORP
- HSTS und HTTPS-Only
- Browser-Wahl und Härtung (Kap 4 — Nutzer-Sicht)