security: Permissions-Policy-Header setzen (Pentest-Finding)

Helmet setzt Permissions-Policy nicht out-of-the-box. Eigene Middleware,
die alle nicht benötigten Browser-APIs deaktiviert:

  camera, microphone, geolocation, payment, usb, midi, hid,
  accelerometer, gyroscope, magnetometer, ambient-light-sensor,
  battery, idle-detection, encrypted-media, picture-in-picture,
  publickey-credentials-get, screen-wake-lock, xr-spatial-tracking,
  web-share, autoplay, display-capture, sync-xhr, clipboard-read,
  cross-origin-isolated  →  alle =()

Erlaubt für 'self':
  clipboard-write  (CopyButton-Komponenten)
  fullscreen       (falls Vorschau in Vollbild geöffnet wird)

Damit hat eingeschleustes JS keinen Zugriff auf sensible Browser-APIs,
selbst wenn XSS irgendwie durchrutschen sollte.

Live-verifiziert: Header gesetzt + sauber formatiert.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 15:25:27 +02:00
parent 096aa63c6f
commit d206b360a6
+39
View File
@@ -97,6 +97,45 @@ app.set('trust proxy', 'loopback');
// - object-src 'none' → keine Flash/<object>/<embed>-Embeds
// - base-uri 'self' → keine <base>-Hijacking-Tricks
// - form-action 'self' → POST-Targets nur auf eigene Origin
// Permissions-Policy: schaltet Browser-APIs aus, die wir nicht brauchen.
// Verhindert, dass eingeschleustes JS Zugriff auf Kamera/Mikro/GPS/Payment etc.
// bekommt. clipboard-write ist 'self' für die CopyButton-Komponenten,
// fullscreen 'self' falls jemand mal eine Vorschau in Vollbild öffnet.
app.use((_req, res, next) => {
res.setHeader(
'Permissions-Policy',
[
'accelerometer=()',
'ambient-light-sensor=()',
'autoplay=()',
'battery=()',
'camera=()',
'clipboard-read=()',
'clipboard-write=(self)',
'cross-origin-isolated=()',
'display-capture=()',
'encrypted-media=()',
'fullscreen=(self)',
'geolocation=()',
'gyroscope=()',
'hid=()',
'idle-detection=()',
'magnetometer=()',
'microphone=()',
'midi=()',
'payment=()',
'picture-in-picture=()',
'publickey-credentials-get=()',
'screen-wake-lock=()',
'sync-xhr=()',
'usb=()',
'web-share=()',
'xr-spatial-tracking=()',
].join(', '),
);
next();
});
app.use(
helmet({
contentSecurityPolicy: {