使用 HMAC 或 OpenSSL 进行 URL 签名

发布于 2024-11-29 00:16:26 字数 1513 浏览 2 评论 0原文

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

  • 我将使用 PHPPython 来创建页面,因此我需要能够使用两者之一来签署和验证签名。
  • 我的计划是使用 priv/pub 密钥方案来签署一些数据,并能够验证签名是否有效,但这就是事情变得复杂的地方:
  • 验证发生时数据是未知的(它不仅仅是 < code>somearg=value&anotherarg=anothervalue)

我的第一反应是使用 OpenSSL,例如使用 RSA 密钥对,通过以下方式执行类似签名的操作:openssl rsautl -sign -inkey private.pem -insensitive-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); 来自 http://php.net/openssl_verify

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


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

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

非常感谢任何大大小小的指示! 预先感谢,

  • 乔什

I'm interested in url signing (e.g. http://.../?somearg=value&anotherarg=anothervalue&sig=aSKS9F3KL5xc), but I have a few requirements which have left me without a solution yet.

  • I'll be using either PHP or Python for pages, so I'll need to be able to sign and verify a signature using one of the two.
  • My plan was to use a priv/pub key scheme to sign some data, and be able to verify that the signature is valid, but here's where it gets complicated:
  • The data is not known when the verification is happening (it is not just somearg=value&anotherarg=anothervalue)

My first instinct was to use OpenSSL, e.g. with a RSA keypair, to do something along the lines of signing by: openssl rsautl -sign -inkey private.pem -in sensitive -out privsigned and verifying based on the privsigned data and key ONLY: openssl rsautl -verify -inkey public.pem -in privsigned -pubin.

Using PHP's openssl_get_privatekey() and openssl_sign() signs the data just fine, but I need to know the (decrypted!) data in order to verify (which I will not have): openssl_get_publickey() and openssl_verify($data, $signature, $pubkeyid); from http://php.net/openssl_verify.

Or am I missing something here?


So I looked into HMAC, but although many hash function are available in both Python and PHP, I'm baffled as to how I'd go about verifying the hash.
PHP's hash_hmac() allows me to create a hash using a "key" (or in this case a string-key). But how do I go about verifying that a hash is valid (i.e. &sig= hasn't just been manually put in by the end user &sig=abcdefg1234.

So to sum up (sorry for the long question): How can I verify that a signature/hash has been made by my server's (cert/string)key (given I can not verify by redoing the hash of said data)? And do you have any preferences as to which route I should chose, Priv/pub-key or HMAC?

Any pointers big or small is greatly appreciated!
Thanks in advance,

  • Josh

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

看春风乍起 2024-12-06 00:16:26

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

As Henning Makholm pointed out, HMAC is a better choice than public key. There are some best practices you should consider for your particular scenario that will impact your choices:

  • Do you want to consider the hostname and scheme (http/https) in the signature? Maybe.
  • Do you want to consider the path in the signature? Probably.
  • Do you want to consider the query string in the signature? Probably.
  • Do you want to normalize the argument order and escaping prior to signing? Usually not.
  • Do you want to embed signature time etc (to create time-limited URLs)?
  • Do you want to tie the signed URL to some other user state, such as cookie?
  • Are you using user-generated or user-visible content directly in the HMAC? If so, you should "salt" the key using a value that is randomized for each request.

When computing the signature, you'll need to encode it in a URL-friendly way (base64 and base32 are popular choices) and choose an HMAC algorithm (such as SHA-256), and decide how many bits of the signature you want to keep (truncating the HMAC value in half is usually safe). If you choose base64, beware of the different alphabets used by url-safe vs non-url-safe implementations.

Here is a pseudocode implementation (w/o error checking or salting etc) for signing path + query string:

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 is recommended and is available in all common languages.

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);
只为守护你 2024-12-06 00:16:26

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

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

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

HMAC is a symmetric algorithm, so there is no separate creation and checking algorithm. To check, you simply compute the hash as it should have been computed originally, and check that the result equals what you actually got from the client. The security rests on the HMAC key only existing on your server.

Unless you need the signatures to be verifiable by someone who doesn't know the secret key, HMAC is probably a better choice than public-key systems, for reasons of efficiency. It can take several milliseconds to create or verify a public-key signature (some years ago I timed one implementation at 15 ms per operation), whereas HMAC is quite fast.

(Oh, and you cannot verify any kind of signature without knowing the data it's supposed to sign. That wouldn't make any sense, as far as I can see).

影子是时光的心 2024-12-06 00:16:26

如果您想使用 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 如下所示。

<一href="http://e.com/api/?valid_until=1378045287.0&auth_user=user&signature=YlZpLFsjUKBalL4x5trhkeEgqE8%3D ">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):如果数据有效,则为 True。否则为假。
  • Reason (list):字符串列表,指示验证错误。

If you want to use HMAC and Python, then:

$ pip install ska

On the client side

from ska import sign_url

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

Produced URL looks like as follows.

http://e.com/api/?valid_until=1378045287.0&auth_user=user&signature=YlZpLFsjUKBalL4x5trhkeEgqE8%3D

On the server side

Note, that in example below request.GET is given as example. It will most likely vary from what's used in your framework (unless you use 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'
)

The validate_signed_request_data produces a SignatureValidationResult object, which basically holds two properties:

  • result (bool): True if data is valid. False otherwise.
  • reason (list): List of strings, indicating validation errors.
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文