Fix Insecure Webhooks in Ktor
Webhooks are essentially unauthenticated POST endpoints unless you implement cryptographic verification. In Ktor, failing to validate the source of a webhook allows attackers to spoof events—like marking an unpaid order as 'paid' or triggering administrative actions. To secure them, you must verify the payload signature (HMAC) using a shared secret and a constant-time comparison.
The Vulnerable Pattern
post("/webhooks/stripe") { // VULNERABILITY: No signature verification // An attacker can POST any JSON to this endpoint val event = call.receive() if (event.type == "payment_intent.succeeded") { orderService.fulfillOrder(event.data.orderId) } call.respond(HttpStatusCode.OK)
}
The Secure Implementation
The secure implementation enforces three critical layers: 1. It retrieves the raw request body as text to ensure the hash matches exactly what the provider sent. 2. It calculates a SHA256 HMAC using a shared secret stored in environment variables. 3. It utilizes MessageDigest.isEqual() for the signature comparison; this is a 'constant-time' operation that prevents attackers from using timing side-channels to brute-force the valid signature byte-by-byte.
post("/webhooks/stripe") { val payload = call.receiveText() val signature = call.request.header("X-Hub-Signature-256") ?: return@post call.respond(HttpStatusCode.Unauthorized)val secret = System.getenv("WEBHOOK_SECRET") val hmac = Mac.getInstance("HmacSHA256") val secretKey = MessageFormatSpec(secret.toByteArray(), "HmacSHA256") hmac.init(secretKey) val expectedSignature = hmac.doFinal(payload.toByteArray()).toHexString() // Use MessageDigest.isEqual for constant-time comparison to prevent timing attacks if (!MessageDigest.isEqual(signature.toByteArray(), expectedSignature.toByteArray())) { return@post call.respond(HttpStatusCode.Forbidden) } val event = Json.decodeFromString<WebhookEvent>(payload) orderService.fulfillOrder(event.data.orderId) call.respond(HttpStatusCode.OK)
}
Your Ktor API
might be exposed to Insecure Webhooks
74% of Ktor 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.