Mass-Assignment-Schutz: Bankkarte/Adresse/Ausweis (Pentest R155)

Die create/update-Services reichten rohen req.body an Prisma durch, wodurch
customerId (Owner) und id (PK) per Update mutierbar waren - staff-only, kein
Cross-Tenant-Bruch, aber echte Integritaetsschwaeche; mit dem neuen
cardNumber-Feld liegt zudem Finanz-PII auf dieser Flaeche.

Fix: explizite Feld-Whitelist im Service (create+update) fuer BankCard,
Address und IdentityDocument - nur benannte Spalten gehen an Prisma, kein
...data/req.body-Spread mehr. Controller-Helper pickBankCardFields haelt
zusaetzlich die Audit-Logs sauber (keine Phantom-Eintraege injizierter Keys).

Verifiziert: updateBankCard mit {customerId:99999, id:88888, bogusField, ...}
-> id+customerId unveraendert, nur cardNumber gesetzt, Fremdfelder ignoriert.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-08-13 16:40:15 +02:00
co-authored by Claude Opus 4.8
parent 0d42bd89e8
commit cb21a2c9be
3 changed files with 93 additions and 8 deletions
+59 -6
View File
@@ -238,10 +238,17 @@ export async function createAddress(
});
}
// Explizite Feld-Whitelist (Mass-Assignment-Schutz, Pentest R155).
return prisma.address.create({
data: {
customerId,
...data,
type: data.type,
street: data.street,
houseNumber: data.houseNumber,
postalCode: data.postalCode,
city: data.city,
country: data.country,
isDefault: data.isDefault,
},
});
}
@@ -272,9 +279,18 @@ export async function updateAddress(
});
}
// Explizite Feld-Whitelist (Mass-Assignment-Schutz, Pentest R155).
return prisma.address.update({
where: { id },
data,
data: {
type: data.type,
street: data.street,
houseNumber: data.houseNumber,
postalCode: data.postalCode,
city: data.city,
country: data.country,
isDefault: data.isDefault,
},
});
}
@@ -309,10 +325,18 @@ export async function createBankCard(
description?: string;
}
) {
// Explizite Feld-Whitelist statt `...data`-Spread: verhindert Mass-Assignment
// (Pentest R155). Sonst könnte roher req.body u.a. `customerId`/`id` setzen.
return prisma.bankCard.create({
data: {
customerId,
...data,
accountHolder: data.accountHolder,
iban: data.iban,
cardNumber: data.cardNumber,
bic: data.bic,
bankName: data.bankName,
expiryDate: data.expiryDate,
description: data.description,
isActive: true,
},
});
@@ -331,9 +355,21 @@ export async function updateBankCard(
isActive?: boolean;
}
) {
// Explizite Feld-Whitelist statt `data`-Passthrough: verhindert Mass-
// Assignment (Pentest R155 roher req.body konnte `customerId`/`id`
// mutieren). Undefined-Felder ignoriert Prisma → Partial-Update bleibt.
return prisma.bankCard.update({
where: { id },
data,
data: {
accountHolder: data.accountHolder,
iban: data.iban,
cardNumber: data.cardNumber,
bic: data.bic,
bankName: data.bankName,
expiryDate: data.expiryDate,
description: data.description,
isActive: data.isActive,
},
});
}
@@ -373,10 +409,17 @@ export async function createDocument(
licenseIssueDate?: Date;
}
) {
// Explizite Feld-Whitelist (Mass-Assignment-Schutz, Pentest R155).
return prisma.identityDocument.create({
data: {
customerId,
...data,
type: data.type,
documentNumber: data.documentNumber,
issuingAuthority: data.issuingAuthority,
issueDate: data.issueDate,
expiryDate: data.expiryDate,
licenseClasses: data.licenseClasses,
licenseIssueDate: data.licenseIssueDate,
isActive: true,
},
});
@@ -395,9 +438,19 @@ export async function updateDocument(
isActive?: boolean;
}
) {
// Explizite Feld-Whitelist (Mass-Assignment-Schutz, Pentest R155).
return prisma.identityDocument.update({
where: { id },
data,
data: {
type: data.type,
documentNumber: data.documentNumber,
issuingAuthority: data.issuingAuthority,
issueDate: data.issueDate,
expiryDate: data.expiryDate,
licenseClasses: data.licenseClasses,
licenseIssueDate: data.licenseIssueDate,
isActive: data.isActive,
},
});
}