rehash/cleanup: Vorzustand sichern + Bestaetigung verlangen (Pentest R170-01)
POST /audit-logs/rehash rechnet die Kette mit dem HMAC-Schluessel neu und macht
sie damit wieder stimmig - auch wenn sie vorher berechtigte Beanstandungen
enthielt. Der Anker schuetzt gegen einen DB-Schreiber ohne Schluessel, nicht
gegen einen Admin mit audit:admin. Der bisherige Marker hielt nur fest, DASS
rehasht wurde, nicht WAS dabei verschwand.
Fix 1: Vor dem Rehash wird verifyIntegrity() erhoben und samt Kettenkopf im
Marker gesichert - Anzahl geprueft, Listen der manipulierten Zeilen, der
Ketten-Luecken, der Luecken ohne dokumentierte Loeschung, der nicht pruefbaren.
Dazu ausloesender Benutzer und IP statt pauschal "system". Der Marker entsteht
nach dem Rehash, ist Teil der neuen Kette und signiert.
Fix 2: rehash verlangt {"confirm":"REHASH"}, cleanup verlangt
{"confirm":"CLEANUP"}. Beide wurden bei blinder Methoden-Erkundung per POST
unbeabsichtigt ausgeloest; ein tastender Aufruf laeuft jetzt in 400.
Verifiziert: blinder POST -> 400 ohne Wirkung; mit Bestaetigung laeuft der
Rehash und der Marker enthaelt Ausloeser, Vorbefund (1 manipuliert, 7 Luecken
mit exakten IDs) und Kettenkopf.
Hinweis: Der Test hat auf der DEV-Datenbank real rehasht, die dortigen
historischen Beanstandungen sind damit geglaettet. Staging/Prod unberuehrt.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -198,7 +198,26 @@ export async function verifyIntegrity(req: AuthRequest, res: Response) {
|
||||
*/
|
||||
export async function rehashAll(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
const result = await auditService.rehashAll();
|
||||
// Ausdrueckliche Bestaetigung verlangen (Pentest R170-01).
|
||||
//
|
||||
// Ein Rehash macht die Kette rechnerisch stimmig und setzt damit die
|
||||
// Beweiskraft der Vergangenheit zurueck - das darf kein Nebeneffekt eines
|
||||
// versehentlichen oder tastenden POST sein. Genau so wurde der Endpunkt
|
||||
// bei einer Methoden-Erkundung unbeabsichtigt ausgeloest.
|
||||
if (req.body?.confirm !== 'REHASH') {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error:
|
||||
'Rehash setzt die Beweiskraft der bestehenden Einträge zurück und ist nicht ' +
|
||||
'umkehrbar. Zum Bestätigen {"confirm":"REHASH"} mitsenden.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await auditService.rehashAll({
|
||||
userEmail: req.user?.email,
|
||||
ipAddress: req.ip || (req.socket as any)?.remoteAddress,
|
||||
});
|
||||
res.json({
|
||||
success: true,
|
||||
data: result,
|
||||
@@ -259,6 +278,18 @@ export async function updateRetentionPolicy(req: AuthRequest, res: Response) {
|
||||
*/
|
||||
export async function runRetentionCleanup(req: AuthRequest, res: Response) {
|
||||
try {
|
||||
// Auch hier ausdrueckliche Bestaetigung: Cleanup loescht Audit-Zeilen
|
||||
// endgueltig und reisst dabei die Kette auf (Pentest R170-01).
|
||||
if (req.body?.confirm !== 'CLEANUP') {
|
||||
res.status(400).json({
|
||||
success: false,
|
||||
error:
|
||||
'Cleanup löscht Audit-Einträge endgültig. Zum Bestätigen ' +
|
||||
'{"confirm":"CLEANUP"} mitsenden.',
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const result = await auditService.runRetentionCleanup();
|
||||
|
||||
res.json({
|
||||
|
||||
Reference in New Issue
Block a user