Fix Insecure Webhooks in Camping
Webhooks in minimalist frameworks like Camping are often low-hanging fruit for attackers. Most devs assume the endpoint is 'hidden' or trust the source IP, but without cryptographic signature verification, an attacker can spoof events to escalate privileges or manipulate data. If you aren't verifying the HMAC signature of the incoming payload, you're essentially leaving a back door open for anyone who finds the URL.
The Vulnerable Pattern
require 'camping'Camping.goes :WebhookApp
module WebhookApp::Controllers class Receiver < R ‘/webhook’ def post # VULNERABLE: No signature verification # Anyone can POST a JSON body to grant themselves premium access data = JSON.parse(request.body.read) user = User.find(data[‘user_id’]) user.update(status: ‘premium’) “OK” end end end
The Secure Implementation
The secure implementation introduces three critical layers of defense. First, it extracts the raw request body before parsing, which is essential because any whitespace difference in JSON parsing would invalidate the HMAC. Second, it calculates a SHA256 HMAC using a pre-shared secret known only to the provider and the application. Third, it utilizes 'Rack::Utils.secure_compare' for the string comparison. This is vital to mitigate timing attacks, where an attacker could otherwise deduce the signature byte-by-byte based on how long the server takes to reject the request.
require 'camping' require 'openssl' require 'rack/utils'Camping.goes :WebhookApp
module WebhookApp::Controllers class Receiver < R ‘/webhook’ # Set this in your environment variables SECRET = ENV[‘WEBHOOK_SECRET’]
def post signature = env['HTTP_X_HUB_SIGNATURE_256'] body = request.body.read # 1. Generate expected signature using HMAC-SHA256 expected = "sha256=" + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), SECRET, body) # 2. Use secure_compare to prevent timing attacks if signature.nil? || !Rack::Utils.secure_compare(expected, signature) return halt 403, "Unauthorized: Signature mismatch" end # 3. Proceed only after verification data = JSON.parse(body) user = User.find(data['user_id']) user.update(status: 'premium') "OK" end
end end
Your Camping API
might be exposed to Insecure Webhooks
74% of Camping 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.