Fix Insecure Webhooks in Dropwizard
Insecure webhooks in Dropwizard applications are a prime target for SSRF and unauthorized state changes. If you process incoming POST requests without verifying the sender's identity via cryptographic signatures, an attacker can spoof events to trigger internal business logic. The goal is to enforce HMAC SHA-256 signature verification on every incoming webhook payload.
The Vulnerable Pattern
@POST
@Path("/events")
@Consumes(MediaType.APPLICATION_JSON)
public Response handleWebhook(EventPayload payload) {
// VULNERABILITY: No signature verification.
// Anyone can POST to this endpoint and trigger internal logic.
db.updateStatus(payload.getId(), payload.getStatus());
return Response.ok().build();
}
The Secure Implementation
The secure implementation shifts from automatic JSON binding to manual verification. First, it extracts the raw request body as a String to ensure the signature is calculated against the exact bytes received. It uses a shared secret to compute a local HMAC SHA-256 hash. Crucially, it utilizes 'MessageDigest.isEqual()' for a constant-time comparison, which mitigates timing attacks that could otherwise leak the signature byte-by-byte. Only after the signature is validated is the payload deserialized and processed.
@POST @Path("/events") public Response handleSecureWebhook( @HeaderParam("X-Webhook-Signature") String signature, String rawPayload) throws Exception {String secret = configuration.getWebhookSecret(); // 1. Recompute the HMAC SHA-256 signature using the raw body Mac hmac = Mac.getInstance("HmacSHA256"); SecretKeySpec secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), "HmacSHA256"); hmac.init(secretKey); byte[] hash = hmac.doFinal(rawPayload.getBytes(StandardCharsets.UTF_8)); String expectedSignature = Base64.getEncoder().encodeToString(hash); // 2. Timing-safe comparison to prevent side-channel attacks if (signature == null || !MessageDigest.isEqual(signature.getBytes(), expectedSignature.getBytes())) { throw new WebApplicationException("Invalid Signature", Status.UNAUTHORIZED); } // 3. Manually deserialize after validation EventPayload payload = objectMapper.readValue(rawPayload, EventPayload.class); db.updateStatus(payload.getId(), payload.getStatus()); return Response.ok().build();
}
Your Dropwizard API
might be exposed to Insecure Webhooks
74% of Dropwizard apps fail this check. Hackers use automated scanners to find this specific flaw. Check your codebase before they do.
Free Tier • No Credit Card • Instant Report
Verified by Ghost Labs Security Team
This content is continuously validated by our automated security engine and reviewed by our research team. Ghost Labs analyzes over 500+ vulnerability patterns across 40+ frameworks to provide up-to-date remediation strategies.