T09 · Insecure Skill Coding Practices
Error
- Location
- webhooks.md:96
- Finding
- Webhook Events Are Acknowledged Before Durable Processing<![CDATA[ ## Vulnerability Details **File Location**: `webhooks.md:96-100` **Vulnerability Type**: Premature webhook acknowledgment and unreliable asynchronous processing **Risk Level**: High ### Vulnerable Code ```typescript // Acknowledge immediately res.status(200).json({ received: true }); // Process async (prevents timeout) processEventAsync(event); ``` ### Technical Analysis The endpoint returns HTTP 200 before the verified event has been processed or placed in a durable queue. Payment service providers interpret the successful response as confirmation that the event was accepted and generally stop retrying it. The asynchronous operation is neither awaited nor shown as being persisted to a durable job queue. If the application terminates, scales down, loses connectivity, or encounters an unhandled rejection after returning the response, the event can be permanently lost. Webhook idempotency does not address this failure mode because there will be no retry after the provider receives HTTP 200. ### Attack Path 1. An attacker with access to a legitimate customer account triggers a billing event at a strategically chosen time, or a normal payment event occurs. 2. The webhook endpoint verifies the event and immediately returns HTTP 200. 3. The application process is interrupted, restarted, or otherwise fails before `processEventAsync(event)` completes. 4. The payment provider records the webhook as delivered and does not retry it. 5. The corresponding subscription, payment, cancellation, or dispute state is omitted from the application database. An attacker cannot forge a Stripe event if signature verification is correctly implemented, but they may exploit operational instability by repeatedly causing legitimate events. The same data-loss condition can also occur without malicious involvement. ### Impact Assessment No direct operating-system privileges are obtained. The affected scope is the application's billing and authorization state. Lost events ...[truncated 292 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Verify the webhook signature and then persist the event to a durable database table or message queue before returning HTTP 200. - Acknowledge the request only after the durable enqueue or transaction succeeds. - Use a unique constraint on the provider event ID to preserve idempotency. - Process persisted events in a monitored worker with bounded retries and a dead-letter queue. - Record explicit states such as `received`, `processing`, `completed`, and `failed`. - Add reconciliation jobs that compare local billing state with the payment provider. - Monitor queue lag, processing failures, and events that remain incomplete beyond a defined threshold. - If no durable queue is available, await processing before returning and allow the provider to retry on failure. ]]>
