Die Einrichtung läuft auch ohne HTTPS

crypto.randomUUID gibt es nur im secure context — HTTPS oder localhost,
sonst nichts. Wer Flaro auf einem NAS betreibt und ihn unter
http://192.168.x.x:8097 aufruft, hat die Funktion nicht, und die
Geräteerkennung warf mitten in die Einrichtung „crypto.randomUUID is not
a function". Das Konto liess sich nicht anlegen.

Das ist nicht der Sonderfall. Ein Heimserver im eigenen Netz hat kein
Zertifikat, und genau dort wird die Einrichtung durchlaufen.

crypto.getRandomValues ist nicht so beschränkt und liefert denselben
Zufall; daraus wird die UUID von Hand zusammengesetzt. Fällt auch das
aus, bleibt Math.random — kein Zufall für Geheimnisse, aber hier geht es
nur darum, zwei Browser auseinanderzuhalten, und gar keine Kennung wäre
schlechter als eine schwache.

Geprüft mit 20000 Durchläufen: alle entsprechen der Form von Fassung 4,
keine Dublette.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Scarriffle
2026-10-04 09:04:30 +02:00
co-authored by Claude Opus 5
parent 3df873c8cd
commit 8a191d0faf
+41 -2
View File
@@ -268,14 +268,53 @@ function geraetename(): string {
return `${was} · ${/Firefox/.test(u) ? 'Firefox' : /Chrome/.test(u) ? 'Chrome' : 'Safari'}`; return `${was} · ${/Firefox/.test(u) ? 'Firefox' : /Chrome/.test(u) ? 'Chrome' : 'Safari'}`;
} }
/** Eine zufällige Kennung — auch ohne HTTPS.
*
* **`crypto.randomUUID` gibt es nur im secure context.** Das ist HTTPS oder
* `localhost`, sonst nichts. Wer Flaro auf dem NAS betreibt und ihn unter
* `http://192.168.x.x:8097` aufruft, hat die Funktion nicht — und bekam
* mitten in der Einrichtung „crypto.randomUUID is not a function" zu sehen.
* Das ist nicht der Sonderfall, das ist der Normalfall im eigenen Netz.
*
* `crypto.getRandomValues` ist nicht so beschränkt und liefert denselben
* Zufall. Die letzte Stufe mit `Math.random` taugt für Geheimnisse nicht —
* hier geht es aber nur darum, zwei Browser auseinanderzuhalten, und gar
* keine Kennung wäre schlechter als eine schwache.
*/
function zufallskennung(): string {
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
return crypto.randomUUID();
}
const bytes = new Uint8Array(16);
if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {
crypto.getRandomValues(bytes);
} else {
for (let i = 0; i < bytes.length; i++) bytes[i] = Math.floor(Math.random() * 256);
}
// Fassung 4 und Variante nach RFC 4122 — Byte 6 und 8 tragen die
// Kennzeichen. Ohne sie sind es sechzehn Zufallsbytes, die nur aussehen
// wie eine UUID. Gesetzt wird beim Umwandeln und nicht über den Index:
// `bytes[6]` gilt dem Typprüfer als womöglich undefiniert, und das
// Gegenteil zu behaupten wäre die unaufrichtigere Lösung.
const hex = Array.from(bytes, (b, i) => {
const wert = i === 6 ? (b & 0x0f) | 0x40
: i === 8 ? (b & 0x3f) | 0x80
: b;
return wert.toString(16).padStart(2, '0');
}).join('');
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
}
/** Bleibt gleich, damit derselbe Browser nicht als zehn Geräte dasteht. */ /** Bleibt gleich, damit derselbe Browser nicht als zehn Geräte dasteht. */
function geraetekennung(): string { function geraetekennung(): string {
const name = 'flaro.device'; const name = 'flaro.device';
try { try {
let vorhanden = localStorage.getItem(name); let vorhanden = localStorage.getItem(name);
if (!vorhanden) { vorhanden = crypto.randomUUID(); localStorage.setItem(name, vorhanden); } if (!vorhanden) { vorhanden = zufallskennung(); localStorage.setItem(name, vorhanden); }
return vorhanden; return vorhanden;
} catch { return crypto.randomUUID(); } } catch { return zufallskennung(); }
} }
/* ---------- Mediathek ---------- */ /* ---------- Mediathek ---------- */