Befunde der Sicherheitspruefung abarbeiten
S1 (hoch): Die OIDC-Rueckkehr verknuepft eine Identitaet mit einem bestehenden Konto nur bei bestaetigter E-Mail — geprueft wurde das auch, nur war die Bestaetigung als `email_verified !== false` berechnet. Damit galt auch ein fehlender Claim als bestaetigt, und genau den schicken manche Anbieter gar nicht. Jetzt zaehlt allein ein ausdrueckliches `true`. S2 (hoch): `isAuthEnabled()` verlangte den Wert "true"; alles andere hiess "aus". Eine vergessene Variable ergab eine Anwendung, in der jeder Besucher alles darf, und nichts sah danach aus. Umgedreht: Abgeschaltet wird nur, wer ausdruecklich "false" setzt. Die Vorgaben in Compose und .env.example stehen auf true, und der Container warnt beim Start mehrzeilig, solange die Anmeldung aus ist. S5 (mittel): Der OIDC-Bootstrap las `user.count()` und legte danach an — zwei gleichzeitige erste Anmeldungen konnten beide Administrator werden. Jetzt in einer serialisierbaren Transaktion, wie die lokale Ersteinrichtung es laengst tut. S6 (niedrig): Die Herkunftspruefung verglich nur den Host, nicht das Schema. Das Schema kommt jetzt aus BASE_URL — die eine Angabe zum Ursprung, ueber die der Aufrufer nicht mitbestimmt. S3, S4 und S7 bleiben offen und stehen begruendet in docs/sicherheit.md. Bei S3 wurde der naheliegende Ausweg geprueft und verworfen: Waehrend der Sperre weiter Passwoerter zu pruefen kostet 128 MiB je Versuch und tauschte eine Sperre gegen ein Konto gegen eine gegen alle. Refs #20
This commit is contained in:
@@ -128,21 +128,30 @@ async function findeOderLegeAn(
|
||||
// Der erste Mensch, der sich überhaupt anmeldet, wird Administrator —
|
||||
// sonst gäbe es bei `OIDC_ONLY=true` niemanden, der die Verwaltung öffnen
|
||||
// kann. Das entspricht der Ersteinrichtung bei der Passwort-Anmeldung.
|
||||
const istErster = (await prisma.user.count()) === 0;
|
||||
|
||||
const angelegt = await prisma.user.create({
|
||||
data: {
|
||||
email: identity.email,
|
||||
name: identity.name,
|
||||
passwordHash: null,
|
||||
oidcSubject: identity.subject,
|
||||
isInstanceAdmin: istAdmin ?? istErster,
|
||||
lastLoginAt: new Date(),
|
||||
memberships: standardgruppe
|
||||
? { create: { groupId: standardgruppe.id, role: config.defaultRole } }
|
||||
: undefined,
|
||||
//
|
||||
// Serialisierbar, aus demselben Grund wie dort: Zwischen „gibt es schon
|
||||
// jemanden?" und dem Anlegen könnte sonst eine zweite Anmeldung
|
||||
// dazwischenkommen, und beide würden sich für die erste halten. Am Ende
|
||||
// stünden zwei Administratoren da, von denen einer ungebeten ist.
|
||||
const angelegt = await prisma.$transaction(
|
||||
async (tx) => {
|
||||
const istErster = (await tx.user.count()) === 0;
|
||||
return tx.user.create({
|
||||
data: {
|
||||
email: identity.email,
|
||||
name: identity.name,
|
||||
passwordHash: null,
|
||||
oidcSubject: identity.subject,
|
||||
isInstanceAdmin: istAdmin ?? istErster,
|
||||
lastLoginAt: new Date(),
|
||||
memberships: standardgruppe
|
||||
? { create: { groupId: standardgruppe.id, role: config.defaultRole } }
|
||||
: undefined,
|
||||
},
|
||||
});
|
||||
},
|
||||
});
|
||||
{ isolationLevel: "Serializable" },
|
||||
);
|
||||
|
||||
return { id: angelegt.id, isActive: angelegt.isActive };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user