Home > database >  Validate Github Webhook HMAC Signature Lucee Coldfusion and CFWheels
Validate Github Webhook HMAC Signature Lucee Coldfusion and CFWheels

Time:03-11

I am trying to verify the signature of my webhooks from github. Following their documentation, there is only an example for ruby:

post '/payload' do
  request.body.rewind
  payload_body = request.body.read
  verify_signature(payload_body)
  push = JSON.parse(payload_body)
  "I got some JSON: #{push.inspect}"
end

def verify_signature(payload_body)
  signature = 'sha256='   OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), ENV['SECRET_TOKEN'], payload_body)
  return halt 500, "Signatures didn't match!" unless Rack::Utils.secure_compare(signature, request.env['HTTP_X_HUB_SIGNATURE_256'])
end

There is a built-in hmac function for Lucee here, and following some ruby examples I have been able to recreate the same hashes for simple messages like "the fox jumps over the thing." However, once I try to recreate the signature using the request payload I never get the right signature. I have triple checked my secret, and it is consistent with the one used in the webhook, so I am pretty sure the issue lies with how I'm parsing the payload. Below is my current code:

headers = request.wheels.HTTPREQUESTDATA.headers;
signature = structKeyExists(headers, "X-Hub-Signature-256") ? headers["X-Hub-Signature-256"] : "error";
payload = request.wheels.params.payload;
mac = 'sha256=' & lCase(hmac(payload, secret, "HMACSHA256"));

Which results in two different "sha256=..." strings. My payload string (appears to be the correct format):

{"action":"added_to_repository",...

Any advice is appreciated.

CodePudding user response:

I've just created a test Github webhook and was able to successfully verify a push event in Lucee using the following basic code:

boolean function verifySignature( required string signature, required string payload, required string secret ){
 var expectedSignature = "sha256=" & HMAC( arguments.payload, arguments.secret, "HmacSHA256", "utf-8" ).LCase()
 return ( arguments.signature == expectedSignature )
}

requestData = GetHTTPRequestData()
payload = requestData.content.Trim()
signature = requestData.headers[ "X-Hub-Signature-256" ]
secret = "mysecret"
result.verified = verifySignature( signature, payload, secret )
dump( result )

A few small differences with yours:

  • I'm using the GetHTTPRequestData() built-in function directly rather than CFWheels-provided values
  • I've trimmed the payload
  • I've specified utf-8 as the character encoding to the HMAC() function
  • Related