GuardAPI Logo
GuardAPI

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

System Alert • ID: 9655
Target: Camping API
Potential Vulnerability

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.

RUN FREE SECURITY DIAGNOSTIC
GuardLabs Engine: ONLINE

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.