A client-submitted grandTotal that the server uses directly is tamper-able: subtract $5 from the total before submitting and pay less. CWE-602 and OWASP A03 both flag trusting client-calculated aggregates. PCI DSS Req-6.2.4 requires independent server-side validation of all payment amounts. This pattern is particularly risky because the individual components (subtotal, tax, fee) may each be validated correctly while the tampered total bypasses all of them.
Info because exploiting this requires deliberate request manipulation, and the mismatch is often caught by downstream payment processors — but it remains a meaningful integrity gap.
Ignore any total field from the client and reconstruct it server-side from validated components before committing.
// src/app/api/checkout/route.ts
export async function POST(req: Request) {
const { subtotalCents, taxRateBps, feesCents } = schema.parse(await req.json());
// taxRateBps: basis points (e.g., 800 = 8%)
const taxCents = Math.round(subtotalCents * taxRateBps / 10000);
const totalCents = subtotalCents + taxCents + feesCents;
await processPayment(totalCents); // use server-calculated total only
}
Never read totalCents, grandTotal, or equivalent fields from the request body when accepting payment.
ID: finserv-form-validation.calculation-accuracy.total-recalculation
Severity: info
What to look for: Count all API endpoints that accept a calculated total from the client (search for total, grandTotal, finalAmount in request body parsing). For each, check whether the server recalculates the total from components or trusts the client-submitted value. Quote the server-side recalculation logic found (or its absence). Report: "X of Y total-accepting endpoints recalculate server-side."
Pass criteria: At least 100% of endpoints that accept totals recalculate them server-side from component values (subtotal + tax + fees). No more than 0 endpoints trust a client-submitted total without verification.
Fail criteria: Any endpoint uses a client-submitted total directly without recalculating from components.
Skip (N/A) when: The project has no calculated totals (0 endpoints accept total values from clients).
Detail on fail: Example: "Form submits subtotal, tax, and total. Server uses submitted total directly without recalculating from subtotal + tax." or "Only individual components are verified; total is not independently verified"
Cross-reference: The tax-fee-verification check verifies individual fee components; this check verifies the final aggregated total.
Remediation: Recalculate totals server-side in src/app/api/ route handlers:
In src/app/api/checkout/route.ts:
export async function POST(req) {
const { subtotalCents, taxRate, feesCents, submittedTotalCents } = req.body;
// Recalculate server-side
const tax = Math.round(subtotalCents * taxRate);
const calculatedTotal = subtotalCents + tax + feesCents;
if (submittedTotalCents !== calculatedTotal) {
throw new Error('Total mismatch');
}
// Process with verified total
}