Vulnerability Management

HMAC-Signed Webhooks for Real-Time Integration Sync

By PMAP Security Team 18 min read

A webhook is an open door into your vulnerability management platform. When Jira closes an issue, when GitHub pushes a commit, when ServiceNow flips a change request to “implemented,” those events arrive as unsolicited HTTP POST requests. If your platform trusts whatever lands on that endpoint, anyone who learns the URL can rewrite finding status, trigger scans, or flood the orchestrator with forged events. Real-time sync is only an advantage when the platform can prove that each inbound event actually came from the system it claims to come from.

This is where signature verification stops being a checkbox and becomes the load-bearing control. PMAP treats every inbound webhook as untrusted until it proves otherwise. The mechanics differ by vendor because GitHub, GitLab, Jira, and Azure DevOps each sign requests in their own way, but the contract is the same across all of them. A request that cannot prove its origin does not move a single finding. This article walks through how that verification works, why constant-time comparison matters, what happens to unsigned requests in production, and how the result feeds findings in real time without sacrificing the reconciliation safety net underneath.

If you want the broader architectural picture first, the security integration layer pillar explains where webhook receivers sit relative to scanners, ITSM, and CI/CD. This article goes one level down into the verification mechanics.

Why Webhook Signatures Matter for Security Tooling

Most integration documentation treats webhooks as a convenience feature. For a security platform the framing is different. Every inbound webhook is a write path into authoritative state. A Jira webhook can move a finding from “open” to “resolved.” A ServiceNow webhook can change finding status off the back of a vendor state change. A GitHub webhook can fan out a scan and emit a runbook trigger. These are not cosmetic updates. They alter the record that auditors, remediation owners, and risk dashboards rely on.

That changes the threat model. An attacker who can forge an inbound webhook does not need credentials, does not need to authenticate, and does not need to touch the user interface. They only need the receiver URL and the willingness to send POST requests. If the receiver accepts unsigned or improperly signed payloads, the attacker can mark real findings as closed and quietly shrink your visible risk. The integrity of the entire remediation loop depends on the receiver refusing to act on anything it cannot verify.

PMAP’s integration domain is built as a universal connector hub, and inbound webhooks are the push half of how external systems talk back to it. The platform sits upstream of findings, scans, and assets, so a forged event does not stay contained inside the integration layer. It propagates. That is precisely why the receiver applies cryptographic verification before any business logic runs. Verification is the first gate, not an afterthought layered on later.

The industry guidance lines up with this posture. The OWASP REST Security Cheat Sheet calls for authenticating and validating every inbound request rather than trusting network position. HMAC, the mechanism most webhook providers use, is specified in RFC 2104. PMAP implements both the spirit and the letter of that guidance at the receiver boundary.

How PMAP Verifies Each Inbound Hook

PMAP does not apply one universal verification routine to every vendor, because vendors do not sign requests the same way. Some send an HMAC-SHA256 digest in a header. Some send a shared token in a query parameter. Some use a Bearer token. The platform handles this with per-vendor verification rules so that each receiver checks the exact artifact its source actually produces. What stays constant is the outcome. Verification runs before the payload is parsed into a meaningful event, and a failed verification ends the request.

The receivers are split into two families. Inbound ITSM webhooks land at /webhooks/jira/{integrationId}, /webhooks/servicenow/{integrationId}, and /webhooks/manageengine/{integrationId}. Inbound CI webhooks land at /webhooks/ci/{vendor}/{integrationId} for GitHub, GitLab, Jenkins, Azure DevOps, Bamboo, and Bitbucket. Each receiver knows which integration record the path refers to, loads the secret associated with that integration, and applies the verification rule appropriate to the vendor. The secret itself is stored encrypted at rest, so the value used to check a signature is never sitting in plaintext on disk.

Because the integration ID is part of the URL, PMAP can scope verification to a specific connector rather than checking against a single global secret. Two GitHub integrations can hold two different webhook secrets, and an event arriving for one is verified against that one. This per-integration isolation matters when a platform serves multiple teams or multiple repositories that should not share a trust boundary.

GitHub X-Hub-Signature-256 HMAC-SHA256

GitHub is the textbook case of HMAC webhook signing. When you configure a webhook secret in GitHub, every delivery includes an X-Hub-Signature-256 header. That header is an HMAC-SHA256 digest computed over the exact raw request body using the shared secret as the key. PMAP recomputes the same HMAC-SHA256 over the body it received, using its stored copy of the secret, and compares the two digests.

The detail that makes this trustworthy is that the signature covers the body. An attacker cannot tamper with the payload and keep a valid signature, because changing a single byte of the body changes the digest. They also cannot forge a signature for a body they choose, because they do not hold the secret. This is exactly the property RFC 2104 is designed to provide. PMAP’s GitHub receiver leans on it directly and rejects any request whose recomputed digest does not match the header. GitHub’s own webhook signature documentation describes the same X-Hub-Signature-256 scheme that the receiver validates against.

Constant-Time Token Comparison for Jira, GitLab and Others

Not every vendor sends a full HMAC digest. Several send a shared secret token that the receiver compares against its stored value. PMAP handles each in its native form. Jira sends its token as a query parameter, and the receiver compares it using a constant-time comparison. GitLab sends an X-Gitlab-Token header, also checked in constant time. Azure DevOps presents a Bearer token, compared in constant time. Jenkins, Bamboo, and Bitbucket each present their own token artifact, an X-Api-Token value, a UUID, or a Bitbucket signature, and each is verified with constant-time comparison.

The phrase “constant-time” is doing real work here. A naive string equality check returns as soon as it hits the first mismatched character. The time it takes to fail leaks information about how many leading characters were correct, and an attacker who can measure that timing can recover a secret one character at a time. A constant-time comparison always takes the same amount of time regardless of where the values diverge, so it gives the attacker no timing signal to exploit. PMAP uses constant-time comparison for these token checks specifically to close that side channel. It is a small implementation detail with a large security consequence, and it is the kind of thing that separates a receiver written for convenience from one written for adversarial conditions.

The result is a verification layer that meets each vendor where it is. HMAC-SHA256 where the vendor signs the body, constant-time token comparison where the vendor shares a secret value, and no path that skips the check. The vendor decides the artifact. PMAP decides that the artifact must be valid before anything happens.

Rejecting Unsigned Webhooks in Production

The most important rule is also the simplest to state. On a production instance, a webhook that arrives without a configured secret is rejected outright. PMAP gates this on an explicit environment flag. When CI_WEBHOOK_ALLOW_UNSIGNED is not set to true, an inbound hook that lacks a secret to verify against is refused. Every inbound hook. No exceptions carved out for convenience, no quiet acceptance of an unsigned request because the operator forgot to set a secret.

This default matters because the dangerous failure mode for webhook security is the silent one. A receiver that accepts unsigned requests “just to keep things working” turns a missing configuration into an open write path. PMAP inverts that. The absence of a verifiable secret is treated as a reason to reject, not a reason to wave the request through. Security is the default state, and relaxing it requires a deliberate, named environment setting that an operator has to choose on purpose.

The flag exists because there are legitimate non-production situations where you genuinely want unsigned delivery, a local development loop or a throwaway sandbox where you are testing payload shapes and have not wired up secrets yet. Making that an explicit opt-in keeps the convenience available without letting it leak into production by accident. The important property is the direction of the default. You opt into accepting unsigned hooks. You never opt into rejecting them, because rejection is already the baseline.

For teams under audit, this is a clean control to point at. The platform does not accept inbound events it cannot verify, and the only way to change that is a documented environment flag whose value can be inspected. That is a far easier story to defend than “the receiver trusts the network.”

Inbound ITSM Webhooks That Update Findings

Once a webhook passes verification, it does something real. The ITSM receivers are the clearest example of why verification has to come first. The Jira receiver handles issue-updated and comment events. When a Jira issue linked to a finding changes status, the receiver maps that vendor-side state change back onto the PMAP finding and can post notes reflecting the activity. The ServiceNow receiver does the same for state changes, and the ManageEngine receiver for its own status changes. In each case a verified vendor event drives finding status and timeline updates inside PMAP.

This is the payoff of real-time sync. A remediation owner closes a Jira ticket, and the linked finding reflects that almost immediately rather than waiting for the next polling cycle. The lifecycle stays coherent across systems because the two ends are talking continuously. But notice what is gated behind verification. The receiver is changing authoritative finding state from an external signal. If that signal were forgeable, the sync would be a liability rather than a feature. Verification is what makes the update safe to apply.

There is also a deliberate nuance in how a resolved ticket maps to a finding. PMAP does not blindly close a finding the moment a ticket reaches a done state. The behavior is governed by the integration’s auto_close_on_resolve setting. When it is true, a ticket moving to a done status closes the finding directly. When it is false, which is the default, the finding transitions to a retest state so a security analyst verifies the fix before the finding is truly closed. That keeps a human checkpoint in the loop for organizations that want one, and it means a verified webhook drives the workflow forward without short-circuiting validation.

If you want the full two-way ticketing model, including how findings flow out to ITSM in the first place, the connector management and ticketing material in the feature cluster covers the operational side. This article stays on the verification half of that loop.

CI Webhooks Into the Auto-Scan Orchestrator

The CI receivers follow the same verification-first pattern, then feed a different downstream consumer. Inbound CI webhooks from GitHub, GitLab, Jenkins, Azure DevOps, Bamboo, and Bitbucket are verified, normalized into a common CIEvent representation, and handed to the orchestrator. Normalizing six vendors into one event shape is what lets the rest of the platform treat a GitHub push and a Bitbucket push the same way. The orchestrator does not need to know which CI system fired the event. It only needs the normalized event.

From there the orchestrator applies a branch filter, then fans out. It can trigger pipeline runs, kick off SAST or SCA scans against the affected code, or both. Regardless of whether auto-scan is enabled, the orchestrator always emits a runbook trigger event, so automation rules downstream can react even when no scan is configured. The CI event audit log keeps the last 200 events per integration, recording the event type, repository, branch, commit, pull request, the action taken, and crucially the signature validity of each event. That audit trail means an operator can look back and confirm that the events that drove scans were verified, not just accepted.

That signature-validity record closes the loop on accountability. It is not enough to verify silently. PMAP also retains whether each inbound event was properly signed, so the verification decision is observable after the fact. If you are debugging why a scan did or did not fire, or proving to an auditor that the orchestrator only acted on verified events, that log is the evidence.

The fact that six CI vendors converge on one verified, normalized webhook path is itself a design choice worth understanding on its own. The companion piece on six CI/CD vendors under one webhook standard covers the breadth of that integration. Here the point is narrower. Every one of those six receivers verifies before it normalizes, and the normalized event carries its verification status forward.

Push Plus Poll: Webhooks and the Reconciliation Safety Net

Webhooks are fast, and webhooks are also unreliable. Network partitions drop deliveries. Vendor outages delay them. A receiver restart can miss a hook that fired during the gap. Any architecture that relies on push alone will eventually drift out of sync, because a single missed delivery means a vendor-side change that never reached the platform. PMAP does not assume webhooks always arrive. It pairs push with poll.

The TicketPoller runs as a background process every 5 minutes against every active ITSM integration. It pulls status updates since the last poll and mirrors them onto findings, reconciling exactly the kind of delivery failures that webhooks alone would leave behind. If a Jira webhook is lost in transit, the next poll catches the status change anyway. The webhook gives you near-real-time updates in the common case. The poller guarantees eventual consistency in the failure case. Together they give you both speed and durability, which neither mechanism provides on its own.

This is the architectural maturity that distinguishes a production integration layer from a demo. A naive implementation treats webhooks as the source of truth and silently loses data when a delivery fails. PMAP treats webhooks as the fast path and polling as the safety net, so a dropped hook degrades latency rather than correctness. The finding state converges either way. For a security platform where a missed status update could leave a resolved vulnerability showing as open, or worse, an open one showing as resolved, that reconciliation guarantee is not optional.

The same philosophy shows up on the scanner side, where a per-minute scheduler and an orphan-adoption sweep reconcile vendor scans into PMAP. Push for speed, scheduled reconciliation for correctness. The webhook layer is one expression of a consistent design stance across the whole integration domain.

Managing and Rotating the Webhook Secret

A signature is only as trustworthy as the secret behind it. PMAP gives operators a full lifecycle for the webhook secret rather than treating it as a one-time setup value. Through the ITSM webhook management endpoints, an operator can view inbound webhook configuration, register the webhook on the vendor side, run an auto-setup that registers and verifies in one step, regenerate the secret, and reveal the current secret when they need to paste it into a vendor console.

Rotation is the operation that matters most for long-term hygiene. Secrets leak through logs, screenshots, copy-paste into chat, and departing employees. A platform that cannot rotate a webhook secret forces a choice between accepting that risk and tearing the integration down entirely. PMAP’s webhook/regenerate endpoint rotates the secret in place, and the reveal endpoint lets the operator retrieve the new value to update the vendor side. The secret is decrypted on demand for that reveal rather than living in plaintext, which keeps the stored state encrypted even while supporting the operational need to read it back occasionally.

The auto-setup path is worth calling out because it removes a common source of misconfiguration. Wiring a webhook by hand means generating a secret, registering it on the vendor, and confirming the vendor can actually reach the receiver. Each step is a chance to fat-finger a value or skip a verification. The setup endpoint registers and verifies in one motion, so the operator ends up with a webhook that is both configured and confirmed working, not one that looks configured but silently fails on the first real event.

Rotation and verification together mean the trust anchor of the whole webhook layer is maintainable over the life of the integration, not just at the moment it was first wired up. That is the difference between a webhook that is secure on day one and one that stays secure on day three hundred.

How Real-Time Sync Fits the Integration Layer

Webhook verification is not a standalone feature. It is one boundary of PMAP’s universal connector hub, the push-side counterpart to the scanner imports, ITSM ticketing, and CI orchestration that the integration domain coordinates. Inbound events flow in through verified receivers, get normalized, and update findings, scans, or asset state. Outbound, the platform creates tickets, posts PR comments, and reports commit status. The webhook receivers are the part of that loop where external systems initiate contact, which is exactly why they carry the strictest trust requirements.

Seen from the top, the design has a consistent logic. Trust nothing inbound until it proves origin. Verify in the vendor’s native format. Reject anything unverifiable by default. Then, once verified, let the event drive real state and lean on scheduled reconciliation to cover the cases where the fast path fails. That combination is what lets PMAP offer real-time sync without making real-time a synonym for unsafe.

For the architectural overview of how every connector type fits together, including where verified webhooks sit in the larger ingest-and-correlate flow, the security integration layer pillar is the place to start. To see the full webhook and ticketing model end to end, including the ITSM and CI/CD coverage that these receivers support, read the integration platform datasheet.

Verify every inbound event with HMAC. Read the integration platform datasheet for the full webhook model.

Frequently Asked Questions

How does PMAP verify an inbound GitHub webhook?

GitHub sends an X-Hub-Signature-256 header containing an HMAC-SHA256 digest computed over the raw request body using the shared webhook secret. PMAP recomputes the same HMAC-SHA256 over the body it received with its stored copy of the secret and compares the two digests. If they do not match, the request is rejected before any event handling runs. Because the signature covers the full body, tampering with the payload invalidates the signature.

What is constant-time comparison and why does PMAP use it?

Constant-time comparison checks two values in a way that always takes the same amount of time regardless of where they differ. A normal string comparison stops at the first mismatched character, and the timing of that failure can leak how many leading characters were correct, which an attacker can exploit to recover a secret. PMAP uses constant-time comparison for token-based vendors such as Jira, GitLab, Azure DevOps, Jenkins, Bamboo, and Bitbucket to remove that timing side channel.

What happens to a webhook that arrives without a valid signature?

On a production instance, an inbound webhook with no configured secret to verify against is rejected. PMAP gates this on the CI_WEBHOOK_ALLOW_UNSIGNED flag. When that flag is not set to true, every unsigned inbound hook is refused. Rejection is the default state, and accepting unsigned hooks requires deliberately setting that environment flag, which is intended for non-production scenarios only.

Do all vendors sign webhooks the same way?

No, and PMAP does not force them to. GitHub signs the body with HMAC-SHA256 in a header. Jira sends a token as a query parameter. GitLab uses an X-Gitlab-Token header. Azure DevOps uses a Bearer token. Jenkins, Bamboo, and Bitbucket each present their own token artifact. PMAP applies the verification rule that matches each vendor’s native format, while keeping the same outcome that an unverifiable request never drives any state change.

How do verified webhooks update findings?

After verification, the ITSM receivers map vendor-side status changes onto linked findings and can post notes reflecting the activity. Whether a resolved ticket closes the finding depends on the integration’s auto_close_on_resolve setting. When true, a done-status ticket closes the finding directly. When false, the default, the finding moves to a retest state so a security analyst verifies the fix first.

What happens if a webhook delivery is lost?

PMAP pairs push with poll. The TicketPoller runs every 5 minutes against all active ITSM integrations and pulls status updates that webhooks may have missed, reconciling delivery failures. The webhook gives near-real-time updates in the common case, and the poller guarantees the finding state eventually converges even when a hook is dropped. Neither mechanism alone provides both speed and durability, so PMAP runs both.

Can the webhook secret be rotated?

Yes. PMAP exposes endpoints to regenerate the webhook secret in place, reveal the current value when an operator needs to update the vendor side, register the webhook on the vendor, and run an auto-setup that registers and verifies in one step. The secret is stored encrypted at rest and decrypted on demand only when an operator explicitly reveals it, so rotation is fully supported without leaving the secret in plaintext.

author avatar
PMAP Security Team

Newsletter

Get the next writeup in your inbox

One short email when a new case writeup or detection deep dive ships. No marketing drip, no third-party tracking.