Dienstkonto-Kennzeichen gegatet und laut protokolliert (Pentest R184)

Mit dem Scharfschalten des Feldes wurde ein alarm-senkendes Attribut ueber den
normalen Benutzer-Update-Pfad setzbar - dieselbe Klasse wie R183-01, nur neu
gebaut. Wer sein eigenes Konto so markiert, laesst die eigenen auffaelligen
Anmeldungen als Routine erscheinen.

Alle drei Sorgen des Pentesters bestaetigt: kein Gate, Protokollierung nur als
MEDIUM (Standardstufe fuer User), und jeder mit users:update konnte es auf
jedes Konto setzen, auch auf das eigene.

Fix analog zur Retention-Absenkung: Bestaetigung confirm SERVICE_ACCOUNT beim
Aktivieren; CRITICAL statt MEDIUM mit eigenem Label und Wer/Vorher/Nachher;
kein Selbstbedienen (403 am eigenen Konto, muss ein anderer Administrator
vornehmen). Das Frontend sendet die Bestaetigung mit - der Haken im Formular
ist die Bestaetigung.

Verifiziert ueber den echten Controller: ohne Bestaetigung 400, mit 200, am
eigenen Konto 403, Protokolleintrag CRITICAL mit sprechendem Label.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-24 11:59:06 +02:00
co-authored by Claude Opus 5
parent 389fd30094
commit b3a9ef6372
4 changed files with 81 additions and 4 deletions
+8
View File
@@ -328,6 +328,10 @@ function UserModal({
hasDeveloperAccess: formData.hasDeveloperAccess,
hasGdprAccess: formData.hasGdprAccess,
isServiceAccount: formData.isServiceAccount,
// Das Kennzeichen senkt die Alarmstufe der Anmeldungen dieses Kontos.
// Der Server verlangt dafür eine ausdrückliche Bestätigung; der Haken
// im Formular IST diese Bestätigung.
...(formData.isServiceAccount ? { confirm: 'SERVICE_ACCOUNT' } : {}),
whatsappNumber: formData.whatsappNumber || undefined,
telegramUsername: formData.telegramUsername || undefined,
signalNumber: formData.signalNumber || undefined,
@@ -364,6 +368,10 @@ function UserModal({
hasDeveloperAccess: formData.hasDeveloperAccess,
hasGdprAccess: formData.hasGdprAccess,
isServiceAccount: formData.isServiceAccount,
// Das Kennzeichen senkt die Alarmstufe der Anmeldungen dieses Kontos.
// Der Server verlangt dafür eine ausdrückliche Bestätigung; der Haken
// im Formular IST diese Bestätigung.
...(formData.isServiceAccount ? { confirm: 'SERVICE_ACCOUNT' } : {}),
whatsappNumber: formData.whatsappNumber || undefined,
telegramUsername: formData.telegramUsername || undefined,
signalNumber: formData.signalNumber || undefined,
+2 -2
View File
@@ -1548,11 +1548,11 @@ export const userApi = {
const res = await api.get<ApiResponse<User>>(`/users/${id}`);
return res.data;
},
create: async (data: { email: string; password: string; firstName: string; lastName: string; roleIds: number[]; customerId?: number; hasDeveloperAccess?: boolean; hasGdprAccess?: boolean; isServiceAccount?: boolean; whatsappNumber?: string; telegramUsername?: string; signalNumber?: string }) => {
create: async (data: { email: string; password: string; firstName: string; lastName: string; roleIds: number[]; customerId?: number; hasDeveloperAccess?: boolean; hasGdprAccess?: boolean; isServiceAccount?: boolean; confirm?: string; whatsappNumber?: string; telegramUsername?: string; signalNumber?: string }) => {
const res = await api.post<ApiResponse<User>>('/users', data);
return res.data;
},
update: async (id: number, data: Partial<User> & { password?: string; roleIds?: number[] }) => {
update: async (id: number, data: Partial<User> & { password?: string; roleIds?: number[]; isServiceAccount?: boolean; confirm?: string }) => {
const res = await api.put<ApiResponse<User>>(`/users/${id}`, data);
return res.data;
},