Verifying request of Webhook

I have a Rails application that is set to receive a webhook from WooCommerce. Specifically I am looking for when an order is created. I have tested and verified that it works when I have protect_from_forgery except create. Now I am trying to secure my application by verify the webhook. WooCommerce’s documentation states the following secret is passed in the request header:

secret: an optional secret key that is used to generate a HMAC-SHA256 hash of the request body so the receiver can verify authenticity of the web hook

Read More

WooCommerce github doc

At the moment I am not sure how I am suppose to verify the request, then act on it. And if the request is not authorized reject it with a 401. Here is what I am trying:

class HooksController < ApplicationController

    protect_from_forgery
    before_action :restrict_access

    def order_created_callback
    ...
    end

    private

    SHARED_SECRET = 'my_secret_key'

    def verify_webhook(data, hmac_header)
        digest  = OpenSSL::Digest::Digest.new('sha256')
        calculated_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, SHARED_SECRET, data)).strip
        calculated_hmac == hmac_header
    end

    def restrict_access
        data = request.body.read
        verified = verify_webhook(data, env["X-WC-Webhook-Signature"])
        head :unauthorized unless verified

    end
end

But so far I have been unsuccessful. Any input would be greatly appreciated. Thanks.

Related posts

Leave a Reply

1 comment

  1. Ok I figured out the issue I was having. In case anyone else is trying to work with WooCommerce web hook it seems my issue was appropriately grabbing the header file of the request to match it against my calculated HMAC.

    SHARED_SECRET = "my_secret"
    
    def verify_webhook(data, hmac_header)
        hash  = OpenSSL::Digest::Digest.new('sha256')
        calculated_hmac = Base64.encode64(OpenSSL::HMAC.digest(hash, SHARED_SECRET, data)).strip
        Rack::Utils.secure_compare(calculated_hmac, hmac_header)
    end
    
    def restrict_access
        data = request.body.read
        head = request.headers["X-WC-Webhook-Signature"]
        verified = verify_webhook(data, head)
        if verified
            return
        else
            render nothing: true, status: :unauthorized
        end
    end