SOC webhooks: a valid signature does not prove a new event
Separate signatures, freshness, identity and processing outcomes so that a webhook retransmission does not trigger the same action twice.
Integration scope: Seculogik uses a source-agnostic architecture to ingest security alerts and events through its own data map. Wazuh and OpenSearch are the first integrations. Other named sources describe planned connector targets; their availability must be confirmed for each evaluation.
A connector receives a signed notification, opens a case and starts the intended follow-up. The sender does not receive the expected response and sends the notification again. Its signature may still be correct even though work has already begun. Cryptographic validation alone is therefore insufficient grounds to repeat the action.
When receiving a webhook, we recommend separating four questions: is the message authenticated under the intended contract, is delivery timing acceptable, which occurrence does it represent, and which processing actually completed? This method applies across providers. The documented examples below are not announcements of available integrations.
1. Verify the message before letting it drive a decision
GitHub documents a signature calculated from the payload and a shared secret, to be checked before further processing. This provides assurance under that contract, assuming the secret remains protected. It does not independently decide which business action to authorise. Source: GitHub delivery validation
Our recommendation is to describe the reception contract for each source. Identify the endpoint, environment, organisation, accepted message types, expected secret and elements actually covered by the signature. An identifier in a header does not automatically become authenticated evidence. Verify its protection and purpose before making it a trusted key.
An incoming request should not freely select the identity or scope under which it will be processed. Match it to authorised connector configuration. After authentication, still check the event type, requested action, required fields and affected resources. An authentic message can remain outside the intended purpose.
2. Preserve what verification needs
Stripe’s documentation specifies the received raw body and endpoint-specific secret for verification; changing content first can cause the check to fail. A test endpoint’s secret and another endpoint’s secret are not interchangeable. Source: Stripe verification errors
We recommend checking the entire path to the receiver: gateway, proxy, library and body handling. A “clean-up” step that reconstructs content before validation may change what was signed. Use the documented procedure for the provider and library versions actually deployed.
When verification fails, preserve a useful reason without exposing secrets or routinely copying the entire body into logs. Distinguish malformed content, a missing signature, an incorrect signature and configuration problems. A single rejection counter cannot explain which stage needs attention.
3. Separate delivery freshness from event identity
Stripe includes a timestamp in signed data to support a freshness check. A retry receives a new signature and timestamp. A recent delivery can therefore concern an event already received. Source: Stripe event reception
Our recommendation is to record event time, delivery time and local receipt time separately. Where a protocol provides verifiable freshness, enforce its check with a reliable clock and a tolerance appropriate to the contract. Do not solve recovery difficulties by silently removing that check.
Identity needs its own examination. GitHub states that its delivery header retains the same value when redelivery is requested. This illustrates why recovery should not be treated as a new occurrence merely because it arrives later. Source: GitHub best practices
Define the tracking key from the source contract and its uniqueness scope: provider, organisation, environment or subscription as appropriate. Prevent an unvalidated identifier from reserving that key and subsequently blocking legitimate processing. Conflicting content under one identity should produce an explicit anomaly state.
4. Track the actual effect rather than a “received” flag
We recommend distinct states for accepted reception, durable recording, processing started and confirmed outcome. A marker written before a failed persistence operation can lose an event. An action completed without its result being recorded can be repeated after restart. Examine both failure windows.
Recovery should prevent retransmission from multiplying an effect already achieved, while allowing interrupted work to finish. This may require a tracking reference from the destination system or a state check before retrying. The method depends on the action: adding a comment, opening a case and changing a status have different consequences.
The HTTP response also needs a clear contract. Define what has been durably accepted before acknowledging receipt, and how an asynchronous processing failure will become visible. A successful response should not be presented to the SOC as proof that the final action completed.
Stripe also documents duplicate deliveries and no guarantee of arrival order. This source-specific behaviour is a reason to examine ordering assumptions; it does not define every provider’s contract. Source: Stripe delivery behaviour
For notifications describing state changes, specify how an old message will be reconciled with the authoritative current state. Do not let a late arrival silently restore an outdated state.
5. Test recovery without unwanted real effects
Our recommendation is to use an authorised environment, synthetic data and a test action without business consequences. Define expected outcomes before testing:
- A valid message triggers expected processing within the correct scope.
- A missing or incorrect signature, or altered content, is rejected.
- An excessively old delivery follows the documented freshness policy.
- Two deliveries of one event do not apply the same effect twice.
- Failure occurs before persistence, then after the action but before local confirmation.
- Two messages arrive in a different order from the one processing assumed.
- One identity carries conflicting content or concerns another scope.
Entirely fictional example: a notification should open one test case. A simulated failure occurs after case creation but before the result is recorded locally. During recovery, the control should find that case and continue the intended follow-up, rather than create another by default. Check the result in the destination test system, not only the receiver’s logs.
Retain a positive control after rejection tests too. An unavailable endpoint must not be mistaken for a protection that correctly rejects non-compliant messages.
6. Make recovery understandable to the on-call team
The record should connect the source, validated identity, associated deliveries, processing states and confirmed effect. Identify the owner of unresolved failures, recovery method and criterion permitting another attempt. Retained bodies and signatures should stay in authorised locations for an appropriate period.
Repeat the tests after changes to the proxy, library, protocol, secret handling or destination system. Acceptance is concrete: explain why a message was accepted, which event it represents, and why its effect was applied once or recovered without duplication.