Kasse und Google-Anmeldung in der Inhaltsrichtlinie freigegeben
Fortsetzung des Musik-Fundes: PayPal und Google haetten an derselben Stelle still versagt, sobald Dogi seine Zugangsdaten eintraegt -- leerer Bereich statt Bezahlknopf, ohne Fehlermeldung, ohne Protokolleintrag. Vorgehen bewusst gemessen statt geraten: Erst die Angaben der Anbieter (PayPal "Best Practices", Google CSP-Abschnitt der Setup-Anleitung), dann im echten Browser mit PayPals offizieller Testkennung "client-id=test" nachgemessen und die Verstoesse ueber das Ereignis securitypolicyviolation eingesammelt. Die Messung hat zwei Dinge gefunden, die in keiner Anleitung standen: - www.sandbox.paypal.com (frame-src + connect-src). Beim Einrichten testet man mit Sandbox-Zugangsdaten; ohne diesen Eintrag haette die Kasse in genau dieser Phase nicht abgeschlossen werden koennen. - accounts.google.com/gsi/style (style-src). 'unsafe-inline' deckt das NICHT ab -- es erlaubt nur Stile im Dokument, keine nachgeladene Stilvorlage. Der Anmelde-Knopf waere unformatiert erschienen. Bewusst einzelne Adressen statt PayPals vorgeschlagener Platzhalter (*.paypal.com): Was die Messung nicht gebraucht hat, steht nicht drin. Skripte bleiben ohne 'unsafe-inline' -- die Pruefsummen-Loesung fuer die eigenen Inline-Bloecke bleibt unangetastet, und ein zusaetzliches 'unsafe-inline' waere neben Pruefsummen ohnehin wirkungslos. Neuer Test pruef-kasse.mjs (15 Pruefungen): laedt das echte PayPal-SDK, baut die Bezahlknoepfe wirklich auf, rendert den echten Google-Knopf und verlangt NULL Verstoesse. Bestandstest pruef-inhaltsrichtlinie.mjs (22) und pruef-musik.mjs (17) bleiben gruen -- inklusive der Gegenprobe, dass eingeschleuste Skripte weiterhin blockiert werden. Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
@@ -50,6 +50,61 @@ import crypto from "crypto";
|
||||
import { readFileSync, statSync } from "node:fs";
|
||||
import { join, normalize } from "node:path";
|
||||
|
||||
/* =====================================================================
|
||||
FREMDE QUELLEN — hier stehen sie namentlich, nicht verstreut
|
||||
|
||||
Drei Dinge auf dieser Seite kommen nicht vom eigenen Server: die
|
||||
Spotify-Player im Musik-Feld, die PayPal-Kasse und die
|
||||
Google-Anmeldung auf abonnieren.html.
|
||||
|
||||
Warum sie hier zusammenstehen: Wer eine Quelle vergisst, merkt es
|
||||
NICHT beim Deploy und nicht im Server-Protokoll. Der Browser wirft
|
||||
das Element still weg -- genau so ist am 26.08.2026 der Musik-Knopf
|
||||
ausgefallen und erst am 27.08. aufgefallen. Eine Liste an einer
|
||||
Stelle, mit Begründung je Eintrag, ist die einzige Stelle, an der man
|
||||
beim nächsten Mal nachsieht.
|
||||
|
||||
Ausgewählt wurde nach den Angaben der Anbieter (PayPal: "Best
|
||||
Practices" der JS-SDK-Doku, Google: CSP-Abschnitt der Setup-Anleitung)
|
||||
und danach mit pruef-kasse.mjs im echten Browser nachgemessen: Die
|
||||
Kasse wird dort mit PayPals offizieller Testkennung wirklich
|
||||
aufgebaut, die Anmeldung wirklich gerendert. Was dabei nicht gebraucht
|
||||
wurde, steht hier auch nicht -- deshalb einzelne Adressen statt der
|
||||
von PayPal vorgeschlagenen Platzhalter *.paypal.com.
|
||||
===================================================================== */
|
||||
|
||||
/* Spotify: die zwei Player im Musik-Feld (main.js, jede Seite). */
|
||||
const SPOTIFY = "https://open.spotify.com";
|
||||
|
||||
/* PayPal: das SDK selbst liegt auf www.paypal.com, Bilder/Schriften der
|
||||
Knöpfe auf www.paypalobjects.com. Der Bezahlvorgang läuft anschließend
|
||||
in einem Rahmen von www.paypal.com.
|
||||
|
||||
⚠️ Die Übungs-Umgebung (www.sandbox.paypal.com) steht bewusst MIT
|
||||
drin, obwohl im Betrieb nur www.paypal.com gebraucht wird. Grund:
|
||||
Beim Einrichten testet man zuerst mit Sandbox-Zugangsdaten -- das SDK
|
||||
spricht dann ausschließlich mit der Sandbox-Adresse. Ohne diesen
|
||||
Eintrag hätte Dogi beim ersten Testkauf eine Kasse gesehen, die sich
|
||||
nicht abschließen lässt, ohne jeden Hinweis auf den Grund. Genau
|
||||
dieser Verstoß ist bei der Messung mit PayPals Testkennung
|
||||
aufgetreten -- geraten hätte man ihn nicht. */
|
||||
const PAYPAL_SKRIPT = "https://www.paypal.com https://www.paypalobjects.com";
|
||||
const PAYPAL_RAHMEN = "https://www.paypal.com https://www.sandbox.paypal.com";
|
||||
const PAYPAL_BILDER = "https://www.paypal.com https://www.paypalobjects.com";
|
||||
const PAYPAL_VERBINDUNG = "https://www.paypal.com https://www.paypalobjects.com https://www.sandbox.paypal.com";
|
||||
|
||||
/* Google-Anmeldung: Google nennt ausdrücklich die ELTERN-Adresse
|
||||
.../gsi/ statt einzelner Endpunkte -- damit die Anmeldung nicht bricht,
|
||||
wenn Google intern eine Adresse ändert. */
|
||||
const GOOGLE_SKRIPT = "https://accounts.google.com/gsi/client";
|
||||
const GOOGLE_RAHMEN = "https://accounts.google.com/gsi/";
|
||||
const GOOGLE_VERBINDUNG = "https://accounts.google.com/gsi/";
|
||||
/* Der Anmelde-Knopf holt sein Aussehen als eigene Stilvorlage. Wichtig:
|
||||
'unsafe-inline' weiter unten deckt das NICHT ab -- das erlaubt nur
|
||||
Stile IM Dokument, keine nachgeladene Datei von fremder Adresse. Ohne
|
||||
diesen Eintrag erscheint der Knopf unfertig/unformatiert. */
|
||||
const GOOGLE_STIL = "https://accounts.google.com/gsi/style";
|
||||
|
||||
/* Gemerkte Prüfsummen je Datei, zusammen mit der Änderungszeit, aus der
|
||||
sie stammen. Ändert sich die Datei, wird neu gerechnet. */
|
||||
const merker = new Map();
|
||||
@@ -151,13 +206,13 @@ export function inhaltsrichtlinie(wurzel) {
|
||||
wenn ohnehin etwas grundlegend nicht stimmt. */
|
||||
if (!summen) return next();
|
||||
|
||||
const skript = ["'self'", ...summen].join(" ");
|
||||
const skript = ["'self'", ...summen, PAYPAL_SKRIPT, GOOGLE_SKRIPT].join(" ");
|
||||
|
||||
res.setHeader("Content-Security-Policy", [
|
||||
"default-src 'self'",
|
||||
`script-src ${skript}`,
|
||||
/* Siehe Kopf der Datei: bewusste Ausnahme, eigener Punkt auf der Liste. */
|
||||
"style-src 'self' 'unsafe-inline'",
|
||||
`style-src 'self' 'unsafe-inline' ${GOOGLE_STIL}`,
|
||||
/* data: für eingebettete Kleingrafiken, blob: für selbst erzeugte
|
||||
Vorschauen beim Hochladen.
|
||||
|
||||
@@ -169,12 +224,12 @@ export function inhaltsrichtlinie(wurzel) {
|
||||
ihn: Der Deploy hätte Erfolg gemeldet, der Server wäre
|
||||
gestartet, und der Schaden wäre erst jemandem aufgefallen, der
|
||||
die Seite ansieht. */
|
||||
"img-src 'self' data: blob: https://postfach.dogfather-universe.com",
|
||||
`img-src 'self' data: blob: https://postfach.dogfather-universe.com ${PAYPAL_BILDER}`,
|
||||
"font-src 'self'",
|
||||
/* Die Seite spricht mit dem eigenen Server und mit dem internen
|
||||
Bereich unter postfach. -- mehr nicht. Ein eingeschleustes
|
||||
Skript könnte damit keine Daten nach außen schicken. */
|
||||
"connect-src 'self' https://postfach.dogfather-universe.com",
|
||||
`connect-src 'self' https://postfach.dogfather-universe.com ${PAYPAL_VERBINDUNG} ${GOOGLE_VERBINDUNG}`,
|
||||
/* Kein Flash, keine Java-Applets, keine eingebetteten Objekte. */
|
||||
"object-src 'none'",
|
||||
/* Verhindert, dass ein eingeschleustes <base> alle relativen
|
||||
@@ -209,8 +264,10 @@ export function inhaltsrichtlinie(wurzel) {
|
||||
Google-Anmeldung auf abonnieren.html arbeiten ebenfalls mit
|
||||
Rahmen), muss er hier NAMENTLICH ergänzt werden -- sonst
|
||||
wiederholt sich exakt dieser stille Ausfall an der Kasse.
|
||||
pruef-musik.mjs prüft das im echten Browser mit. */
|
||||
"frame-src https://open.spotify.com",
|
||||
pruef-musik.mjs und pruef-kasse.mjs prüfen das im echten Browser
|
||||
mit -- inklusive der Kasse, die vorher genau hier gescheitert
|
||||
wäre. */
|
||||
`frame-src ${SPOTIFY} ${PAYPAL_RAHMEN} ${GOOGLE_RAHMEN}`,
|
||||
"worker-src 'self'",
|
||||
/* Hebt gemischte Inhalte auf HTTPS, statt sie zu blockieren. */
|
||||
"upgrade-insecure-requests",
|
||||
|
||||
Reference in New Issue
Block a user