使用HMAC或OpenSSL进行URL签名



我对url签名感兴趣(例如http://.../?somearg=value&anotherarg=anothervalue&sig=aSKS9F3KL5xc),但我有一些要求,这让我没有解决方案。

  • 我将使用PHPPython作为页面,所以我需要能够使用两者之一来签名和验证签名。
  • 我的计划是使用一个privv/pub密钥方案来签署一些数据,并能够验证签名是有效的,但这是它变得复杂的地方:
  • 当验证发生时数据不知道(不仅仅是somearg=value&anotherarg=anothervalue)

我的第一直觉是使用OpenSSL,例如使用RSA对,通过:openssl rsautl -sign -inkey private.pem -in sensitive -out privsigned签名并根据privsigned数据和密钥ONLY: openssl rsautl -verify -inkey public.pem -in privsigned -pubin进行验证。

使用PHP的openssl_get_privatekey()openssl_sign()签名数据很好,但我需要知道(解密!)数据,以便验证(我不会有):openssl_get_publickey()openssl_verify($data, $signature, $pubkeyid);从http://php.net/openssl_verify.

还是我遗漏了什么?


所以我研究了HMAC,但是尽管在PythonPHP中都有许多哈希函数可用,但我对如何去验证哈希感到困惑。PHPhash_hmac()允许我使用"键"(或者在本例中是字符串键)创建散列。但是我如何去验证一个哈希是有效的(即&sig=不是由最终用户&sig=abcdefg1234手动输入的)。

所以总结一下(对不起,这个问题很长):我如何验证签名/哈希是由我的服务器(cert/string)密钥制作的(给定我不能通过重新做所述数据的哈希来验证)?我应该选择Priv/public -key还是HMAC,你有什么建议吗?

任何大或小的指针都是非常感激的!提前感谢,

    Josh

正如Henning Makholm指出的那样,HMAC是比公钥更好的选择。对于影响您的选择的特定场景,您应该考虑一些最佳实践:

  • 你想在签名中考虑主机名和方案(http/https)吗?也许。
  • 要考虑签名中的路径吗?可能。
  • 要考虑签名中的查询字符串吗?可能。
  • 您想在签名之前规范参数顺序和转义吗?通常不是。
  • 你想嵌入签名时间等(创建有时间限制的url)吗?
  • 您是否想将签名URL绑定到其他用户状态,例如cookie?
  • 你是直接在HMAC中使用用户生成的还是用户可见的内容?如果是这样,您应该使用针对每个请求随机化的值来"盐化"键。

在计算签名时,您需要以url友好的方式(base64和base32是流行的选择)对其进行编码,并选择HMAC算法(例如SHA-256),并决定要保留签名的多少位(将HMAC值截断一半通常是安全的)。如果选择base64,请注意url安全实现与非url安全实现使用的不同字母。

这是一个伪代码实现(没有错误检查或盐渍等)签名路径+查询字符串:

const secret = ...;
def sign(path, querystring):
  return path + "?" + querystring + "&sig=" + url_encode(base64_encode(hmacsha256(secret, path + "?" + querystring).truncate(16)))
def verify(path, querystring):
  querystring_without_sig = remove_query_parameter(querystring, "sig")
  sig = base64_decode(url_decode(get_query_parameter(querystring, "sig")))
  if hmacsha256(secret, path + "?" + querystring_without_sig)[:16] != sig:
    raise "invalid sig"

推荐使用HMAC SHA256,支持所有常用语言。

Java:

Mac mac = Mac.getInstance("HmacSHA256");
mac.init(secret);
return mac.doFinal(value.getBytes());
Python:

hmac.new(secret, input, hashlib.sha256).digest()
PHP:

hash_hmac("sha256", value, secret);

HMAC是一个对称算法,所以没有单独的创建和检查算法。要进行检查,只需按照最初应该计算的方式计算哈希值,并检查结果是否等于实际从客户机获得的结果。安全性依赖于仅存在于您的服务器上的HMAC密钥。

除非您需要由不知道秘钥的人验证签名,否则出于效率的考虑,HMAC可能是比公钥系统更好的选择。创建或验证公钥签名可能需要几毫秒的时间(几年前,我将每次操作的实现时间设定为15毫秒),而HMAC相当快。

(哦,如果不知道要签名的数据,就无法验证任何类型的签名。在我看来,这没有任何意义。

如果你想使用HMAC和Python,那么:

安装ska

客户端

from ska import sign_url
signed_url = sign_url(
    auth_user='user', 
    secret_key='your-secret_key', 
    url='http://e.com/api/'
)

生成的URL如下所示。

http://e.com/api/?valid_until=1378045287.0& auth_user = user&签名= YlZpLFsjUKBalL4x5trhkeEgqE8 % 3 d

服务器端

注意,下面的例子中request.GET是作为例子给出的。它很可能与你的框架中使用的不同(除非你使用Django)。

from ska import validate_signed_request_data
validation_result = validate_signed_request_data(
    data = request.GET, # Note, that ``request.GET`` is given as example.
    secret_key = 'your-secret_key'
)

validate_signed_request_data产生一个SignatureValidationResult对象,它基本上包含两个属性:

  • result (bool):如果数据有效,则为True。虚假的否则。
  • reason (list):字符串列表,表示验证错误。