Verify Shopify webhook

2020-04-18 09:41发布

问题:

I believe that to have a Shopify webhook integrate with a Rails app, the Rails app needs to disable the default verify_authenticity_token method, and implement its own authentication using the X_SHOPIFY_HMAC_SHA256 header. The Shopify docs say to just use request.body.read. So, I did that:

def create
    verify_webhook(request)

    # Send back a 200 OK response
    head :ok
end

def verify_webhook(request)
    header_hmac = request.headers["HTTP_X_SHOPIFY_HMAC_SHA256"]
    digest = OpenSSL::Digest.new("sha256")
    request.body.rewind
    calculated_hmac = Base64.encode64(OpenSSL::HMAC.digest(digest, SHARED_SECRET, request.body.read)).strip

    puts "header hmac: #{header_hmac}"
    puts "calculated hmac: #{calculated_hmac}"

    puts "Verified:#{ActiveSupport::SecurityUtils.secure_compare(calculated_hmac, header_hmac)}"
end

The Shopify webhook is directed to the correct URL and the route gives it to the controller method shown above. But when I send a test notification, the output is not right. The two HMACs are not equal, and so it is not verified. I am fairly sure that the problem is that Shopify is using the entire request as their seed for the authentication hash, not just the POST contents. So, I need the original, untouched HTTP request, unless I am mistaken.

This question seemed like the only promising thing on the Internet after at least an hour of searching. It was exactly what I was asking and it had an accepted answer with 30 upvotes. But his answer... is absurd. It spits out an unintelligible, garbled mess of all kinds of things. Am I missing something glaring?

Furthermore, this article seemed to suggest that what I am looking for is not possible. It seems that Rails is never given the unadulterated request, but it is split into disparate parts by Rack, before it ever gets to Rails. If so, I guess I could maybe attempt to reassemble it, but I would have to even get the order of the headers correct for a hash to work, so I can't imagine that would be possible.

I guess my main question is, am I totally screwed?

回答1:

The problem was in my SHARED_SECRET. I assumed this was the API secret key, because a few days ago it was called the shared secret in the Shopify admin page. But now I see a tiny paragraph at the bottom of the notifications page that says,

All your webhooks will be signed with ---MY_REAL_SHARED_SECRET--- so you can verify their integrity.

This is the secret I need to use to verify the webhooks. Why there are two of them, I have no idea.



回答2:

Have you tried doing it in the order they show in their guides? They have a working sample for ruby.

def create
  request.body.rewind
  data = request.body.read
  header = request.headers["HTTP_X_SHOPIFY_HMAC_SHA256"]
  verified = verify_webhook(data, header)

  head :ok
end

They say in their guides:

Each Webhook request includes a X-Shopify-Hmac-SHA256 header which is generated using the app's shared secret, along with the data sent in the request.

the keywors being "generated using shared secret AND DATA sent in the request" so all of this should be available on your end, both the DATA and the shared secret.