HTTP 签名 - HMAC-SHA256

问题描述 投票:0回答:1

我从 Manager One API 收到传入传输事件 Webhook。对于签名验证,他们采用HMAC-SHA256 算法。他们为我提供了一个共享密钥,它是我放在 ENV[“MANAGER_ONE_SECRET_SIGNATURE”] 中的数字和字母的组合。对于此示例,我们假设该值为“shared_secret_key”。我一直致力于将从请求标头信息中获取的密钥与此共享密钥进行匹配,以成功验证 Webhook。

这是我迄今为止所取得的成就:

class Api::ManagerOneController < Api::ApplicationController
  def create
    if expected_signature == provided_signature_base64
      request_body = JSON.parse(request.raw_post)["incoming_bank_operation"]
      ManagerOne::Webhooks::IncomingTransferJob.perform_later(request_body)
      render status: :ok, json: { message: "Signature verified successfully" }
    else
      render status: :unauthorized, json: { error: "Unauthorized" }
    end
  end

  private

  def expected_signature
    Base64.strict_encode64(
      OpenSSL::HMAC.digest(
        OpenSSL::Digest.new("sha256"),
        ENV["MANAGER_ONE_SECRET_SIGNATURE"],
        signature_input
      )
    )
  end

  def signature_input
    content_length = "\"content-length\": #{request.headers['Content-Length']}\n"
    user_agent = "\"user-agent\": #{request.headers['User-agent']}\n"
    content_type= "\"content-type\": #{request.headers['Content-Type']}\n"
    host = "\"host\": #{request.headers['Host']}\n"
    request_target = "\"(request-target)\": #{"post #{request.path}"}\n"
    date = "\"date\": #{request.headers['Date']}\n"
    digest = "\"digest\": #{request.headers['Digest']}\n"
    signature_params = "\"@signature-params\": (\"content-length\" \"user-agent\" \"content-type\" \"host\" \"(request-target)\" \"date\" \"digest\"); keyid=\"#{ENV["MANAGER_ONE_SECRET_SIGNATURE"]}\"; algorithm=\"hmac-sha256\";"

    "#{content_length}#{user_agent}#{content_type}#{host}#{request_target}#{date}#{digest}#{signature_params}"
  end

  def provided_signature_base64
    request.headers['Signature'].match(/signature="([^"]+)"/)[1]
  end
end

标头随每个请求而变化;我提供的数据只是一个例子:

expected_signature = "1zmbBUVFEsqlj8G0h8SPdikbu7et4boR/r/HHoLkr2o="

我按照本文档中提供的说明生成了signature_input:

signature_input = "\"content-length\": 1107\n\"user-agent\": manager.one\n\"content-type\": application/json\n\"host\": localhost:3000\n\"(request-target)\": post /api/manager_one/webhooks\n\"date\": Mon, 30 Oct 2023 13:02:55 GMT\n\"digest\": SHA-256=sTz9SzsDH3WGcbYSZb+G8Xk1javIUxE0egQaKD1Wyws=\n\"@signature-params\": (\"content-length\" \"user-agent\" \"content-type\" \"host\" \"(request-target)\" \"date\" \"digest\"); keyid=\"shared_secret_key\"; algorithm=\"hmac-sha256\";"
我应该与预期签名匹配的密钥是我从 Manager One API 文档示例中获得的密钥,尽管它会随每个请求而变化:

provided_signature_base64 = "pkLgUJmM5FixErazNavlwWizmDZdTDBcMZb9xY3ZUv8="
我错过了什么吗?我不知道我做错了什么。

ruby-on-rails openssl webhooks hmacsha256 http-signature
1个回答
0
投票
我已经弄清楚了;我的签名输入有几个问题:

    API 文档缺乏如何创建正确签名输入的说明
  • 由于我使用 Ngrok,所以我收到的主机是“localhost:8000”,但我应该使用 Ngrok URL。
  • 我以错误的方式对数据进行哈希处理以匹配预期的签名
最终我解决了这些问题并得出了正确的解决方案:

class Api::ManagerOneController < Api::ApplicationController def create success = expected_signature == provided_signature_base64 request_body = JSON.parse(request.raw_post)["incoming_bank_operation"] ManagerOne::Webhooks::IncomingTransferJob.perform_later(request_body, success) render status: :ok, json: { success: true, message: success ? "Authorized" : "Unauthorized" } end private def expected_signature hmac = OpenSSL::HMAC.digest("SHA-256", ENV["MANAGER_ONE_SECRET_SIGNATURE"], signature_input) Base64.encode64(hmac).chomp end def signature_input content_length = "content-length: #{request.headers['Content-Length']}" user_agent = "user-agent: #{request.headers['User-agent']}" content_type= "content-type: #{request.headers['Content-Type']}" # When using Ngrok in development environment, host must be ngrok url & not localhost:3000 host = "host: #{request.headers['Host']}" request_target = "(request-target): #{"post #{request.path}"}" date = "date: #{request.headers['Date']}" digest = "digest: #{request.headers['Digest']}" "#{content_length}\n#{user_agent}\n#{content_type}\n#{host}\n#{request_target}\n#{date}\n#{digest}" end def provided_signature_base64 request.headers['Signature'].match(/signature="([^"]+)"/)[1] end end
    
© www.soinside.com 2019 - 2024. All rights reserved.