【发布时间】:2023-04-01 01:15:01
【问题描述】:
我正在使用ApiAuth gem 来验证 API 请求。现在我需要编写一个使用 cURL 发送测试请求的 shell 脚本。所以我需要生成 POST 正文的 MD5 并对其进行 base64 编码,以便它与 ApiAuth 在服务器上所做的相匹配:
我的 shell 脚本:
query="{\"document\":{\"recipient_id\":\"$ACCESS_ID\",\"data\":{\"id\":\"$ACCESS_ID\"}},\"vendor_string\":\"test\",\"patient\":{\"document\":{\"recipient_id\":\"$ACCESS_ID\",\"data\":{\"id\":\"$ACCESS_ID\"}}}}"
# need to figure how to get a base64 encoded md5 the same way Ruby does
content_md5=$(echo -n "$query" | openssl md5 -binary | base64)
content_type='application/json'
request_uri="$API_BASE/test"
httpdate=$(date -u +"%a, %_d %b %Y %H:%M:%S GMT")
accept_header='application/vnd.test+json; version=1'
canonical_string="$content_type,$content_md5,$request_uri,$httpdate"
signature=$(echo -n "$canonical_string" | openssl dgst -sha1 -hmac "$SECRET_KEY" -binary | base64)
curl -H "Authorization: APIAuth $ACCESS_ID:$signature"\
-H "Content-MD5: $content_md5" \
-H "Date: $httpdate" \
-H "Accept: $accept_header" \
-H "Content-type: $content_type" \
-d $query \
-v \
$request_uri
失败的第一件事是将我发送的 Content-MD5 与 ApiAuth 计算的内容 MD5 进行比较:
https://github.com/mgomes/api_auth/blob/master/lib/api_auth/base.rb#L37
def authentic?(request, secret_key)
return false if secret_key.nil?
return !md5_mismatch?(request) && signatures_match?(request, secret_key) && !request_too_old?(request)
end
这里md5_mismatch?(request) 方法返回false。它使用这些方法来计算 MD5:
https://github.com/mgomes/api_auth/blob/master/lib/api_auth/request_drivers/action_controller.rb
def calculated_md5
if @request.env.has_key?('RAW_POST_DATA')
body = @request.raw_post
else
body = ''
end
md5_base64digest(body)
end
https://github.com/mgomes/api_auth/blob/master/lib/api_auth/helpers.rb
def b64_encode(string)
if Base64.respond_to?(:strict_encode64)
Base64.strict_encode64(string)
else
# Fall back to stripping out newlines on Ruby 1.8.
Base64.encode64(string).gsub(/\n/, '')
end
end
def md5_base64digest(string)
if Digest::MD5.respond_to?(:base64digest)
Digest::MD5.base64digest(string)
else
b64_encode(Digest::MD5.digest(string))
end
end
所以我认为它归结为完全匹配正在发生的事情:
Digest::MD5.base64digest
我的尝试是:
content_md5=$(echo -n "$query" | openssl md5 -binary | base64)
如何使 bash 脚本等效于 ruby 方法?
我尝试过使用和不使用 -binary 标志。
我检查了 bash 中的 $query 与 Ruby 中的 @request.raw_post 完全相同,并且没有尾随换行符,因为我使用的是 echo -n。
更新:
bash 的输出:
echo $query
{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}},"vendor_string":"kipusystems","patient":{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}}}}
echo $content_md5
Lsb/vxJKHUxyRAqMhOMeOw==
红宝石的输出:
puts body
{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}},"vendor_string":"kipusystems","patient":{"document":{"recipient_id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o","data":{"id":"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o"}}}}
puts md5_base64digest(body)
/DdffT+N+sZZjaTC5TJNcg==
我分别从运行 bash 脚本和 rails 服务器的终端中选择并复制了 $query 和 body 字符串。从这个意义上说,它们都是完全相同的,我该如何进一步缩小这个问题的范围?
更新 2:可能是字符编码问题?
我将此文字粘贴到 (mac bash) shell 提示符中:
echo -n "{\"document\":{\"recipient_id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\",\"data\":{\"id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\"}},\"vendor_string\":\"kipusystems\",\"patient\":{\"document\":{\"recipient_id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\",\"data\":{\"id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\"}}}}" | openssl dgst -md5 -binary | base64
然后输出:/DdffT+N+sZZjaTC5TJNcg== 这很好!这就是 Ruby 端的输出。好酷。
但是,当我使用刚刚在上面粘贴的确切文字命令运行我的 shell 脚本时,它会输出:Lsb/vxJKHUxyRAqMhOMeOw==,这与我最初使用的 content-md5 相同(最初发布的脚本)。
当我运行echo $LANG 时,我得到en_US.UTF-8。
更新 3:
我使用以下命令运行 shell 脚本:
sh script.sh
当我回显这个命令时,它会输出Lsb/vxJKHUxyRAqMhOMeOw==:
echo -n "{\"document\":{\"recipient_id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\",\"data\":{\"id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\"}},\"vendor_string\":\"kipusystems\",\"patient\":{\"document\":{\"recipient_id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\",\"data\":{\"id\":\"lwzvZixLvVLL50qasfoO2YvMz9UzNlxg8HBOEj8NV_o\"}}}}" | openssl dgst -md5 -binary | base64
更新 4:
奇怪!所以,我一直在使用sh script.sh 运行(上面发布的 shell 脚本),这向我展示了一个与我在 Ruby 中看到的不同的 md5 结果。现在,我 chmod +x'ed 脚本并直接运行它:script.sh 现在我得到了正确的 md5!!
但是,现在 ApiAuth 中的 signatures_match? 方法仍然返回 false :'(
【问题讨论】:
-
只是好奇。您是否进行了测试以确保您正在编码的字符串在您的测试双方都是 100% 相同的?我将针对使用 openssl/base64 编码的同一字符串测试字符串的 Ruby Digest::MD5.base64digest,并验证您是否可以首先以这种方式获得相同的结果,以消除输入可能不同的任何可能的测试差异.
-
顺便说一句,出于好奇,为了我的启迪,您是否有理由明确使用 openssl 创建字符串的 md5 散列而不是使用 md5sum?
-
有区别吗?我没有安装 md5sum。
-
是的,正如问题最后一行所解释的,我已经对两边的字符串进行了简单的复制粘贴比较。我相信它们都是 UTF-8,但还没有检查 bash 方面。感谢您的关注,我很难过!
-
脚本中的
echo是否相同? (如果你使用#!/bin/sh,你可能不是。)试试printf '%s' "$query";它在任何情况下都更便携。