【问题标题】:URL Signing with HMAC or OpenSSL使用 HMAC 或 OpenSSL 进行 URL 签名
【发布时间】:2011-10-24 01:23:36
【问题描述】:

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

  • 我将使用PHPPython 作为页面,因此我需要能够使用两者之一进行签名和验证。
  • 我的计划是使用 priv/pub 密钥方案对一些数据进行签名,并能够验证签名是否有效,但这就是复杂的地方:
  • 验证时数据未知(不仅仅是somearg=value&anotherarg=anothervalue

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

使用 PHP 的 openssl_get_privatekey()openssl_sign() 对数据进行签名就好了,但我需要知道(解密!)数据才能验证(我不会拥有):openssl_get_publickey()openssl_verify($data, $signature, $pubkeyid); 来自 @ 987654322@.

或者我在这里遗漏了什么?


所以我研究了 HMAC,但尽管 PythonPHP 都提供了许多哈希函数,但我对如何进行 验证 哈希。 PHPhash_hmac() 允许我使用“键”(或者在本例中为字符串键)创建散列。但是我该如何验证哈希是否有效(即 &sig= 不是由最终用户 &sig=abcdefg1234 手动输入的。

总结一下(对不起,问题太长了):我如何验证我的服务器的(证书/字符串)密钥是否已生成签名/哈希(假设我无法通过重做所述数据的哈希来验证) ?对于我应该选择哪条路线,Priv/pub-key 还是 HMAC,你有什么偏好吗?

非常感谢任何大小的指针! 提前致谢,

  • 乔什

【问题讨论】:

    标签: url verification signing private-key hmac


    【解决方案1】:

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

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

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

    这是一个用于签名路径 + 查询字符串的伪代码实现(无错误检查或加盐等):

    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);
    

    【讨论】:

      【解决方案2】:

      HMAC 是一种对称算法,因此没有单独的创建和检查算法。要检查,您只需计算最初应该计算的散列,并检查结果是否等于您从客户端实际获得的结果。安全性取决于仅存在于您的服务器上的 HMAC 密钥。

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

      (哦,如果不知道它应该签名的数据,你就无法验证任何类型的签名。据我所知,这没有任何意义)。

      【讨论】:

      • 感谢您解决这个问题。我认为简单地做一个“静态”哈希是不安全的,例如哈希(网址)(或哈希(盐+网址))。那时在哈希中使用时间会非常困难,并且参数最终会变得已知。
      • 嗯,这取决于您要防范的是什么,这从您的问题中不清楚。通常,URL 中有一个生成时间参数,该参数包含在哈希输入中并经过检查,以便您可以拒绝陈旧的 URL。
      【解决方案3】:

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

      $ pip install 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&signature=YlZpLFsjUKBalL4x5trhkeEgqE8%3D

      在服务器端

      请注意,在下面的示例中,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):如果数据有效,则为真。否则为假。
      • 原因(列表):字符串列表,表示验证错误。

      【讨论】:

        猜你喜欢
        • 2011-08-17
        • 1970-01-01
        • 2014-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-05
        • 2012-06-02
        相关资源
        最近更新 更多