使用HMAC或OpenSSL进行URL签名

使用HMAC或OpenSSL进行URL签名,url,verification,signing,private-key,hmac,Url,Verification,Signing,Private Key,Hmac,我对url签名感兴趣(例如),但我有一些要求,这些要求使我还没有解决方案 对于页面,我将使用PHP或Python,因此我需要能够使用这两种方法之一对签名进行签名和验证 我的计划是使用priv/pub密钥方案对一些数据进行签名,并能够验证签名是否有效,但这是它变得复杂的地方: 进行验证时,数据未知(不仅仅是somearg=value&anotherarg=anothervalue) 我的第一反应是使用OpenSSL,例如使用RSA密钥对,通过以下方式进行签名:OpenSSL rsautl-si

我对url签名感兴趣(例如),但我有一些要求,这些要求使我还没有解决方案

  • 对于页面,我将使用
    PHP
    Python
    ,因此我需要能够使用这两种方法之一对签名进行签名和验证
  • 我的计划是使用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\u get\u privatekey()
openssl\u sign()
可以很好地对数据进行签名,但我需要知道(解密的!)数据以便进行验证(我不会有):
openssl\u get\u publickey()
openssl\u verify($data,$signature,$pubkeyid)

还是我在这里遗漏了什么


因此,我研究了HMAC,但尽管在
Python
PHP
中都有许多散列函数可用,但对于如何验证散列,我感到困惑。
PHP
hash\u hmac()
允许我使用“键”(或者在本例中是字符串键)创建哈希。但是我如何验证散列是否有效(即,
&sig=
并不是由最终用户手动输入的)

总而言之(很抱歉问了这么长的问题):我如何验证我的服务器的(证书/字符串)密钥是否已进行了签名/哈希(假设我无法通过重做所述数据的哈希进行验证)?对于我应该选择哪种路由,Priv/pub密钥还是HMAC,您有任何偏好吗

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

  • 乔希

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

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


(哦,你不知道任何一种签名都不知道它应该签署的数据。据我所知,这是没有任何意义的。

< P>),正如Henning Makholm指出的,HMAC是比公钥更好的选择。对于你的选择会影响你的选择的一些最佳实践:

    您想在签名中考虑主机名和方案(HTTP/HTTPS)吗?也许。 <>你想考虑签名中的路径吗?可能。 您想考虑签名中的查询字符串吗?可能。
  • 是否要在签名之前规范化参数顺序和转义?通常不会
  • 是否要嵌入签名时间等(以创建有时间限制的URL)
  • 是否要将已签名的URL绑定到其他用户状态,例如cookie
  • 您是否直接在HMAC中使用用户生成的或用户可见的内容?如果是,您应该使用针对每个请求随机化的值来“盐”键
在计算签名时,您需要以URL友好的方式对其进行编码(base64和base32是常用的选择),并选择HMAC算法(如SHA-256),并决定要保留多少位签名(将HMAC值截断一半通常是安全的)。如果选择base64,请注意url安全实现与非url安全实现使用的字母表不同

下面是签名路径+查询字符串的伪代码实现(不带错误检查或salt等):

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,并提供所有通用语言版本

爪哇:

Python:

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


如果要使用HMAC和Python,则:

$pip安装ska

在客户端 生成的URL如下所示

在服务器端 注意,在下面的示例中,
request.GET
作为示例给出。它很可能与框架中使用的内容不同(除非您使用Django)

validate_signed_request_数据生成SignatureValidationResult对象,该对象基本上包含两个属性:

  • 结果(bool):如果数据有效,则为True;否则为False
  • 原因(列表):字符串列表,指示验证错误

谢谢你澄清这一点。我认为简单地对“静态”进行散列是不安全的,例如散列(url)(或散列(salt+url))。在哈希中使用时间将非常困难,参数最终会被知道。好吧,这取决于您试图防范的是什么,这在您的问题中并不清楚。通常情况下,URL中会有一个生成时间参数,该参数包含在哈希输入中并经过检查,以便您可以拒绝过时的URL。
hmac.new(secret, input, hashlib.sha256).digest()
hash_hmac("sha256", value, secret);
from ska import sign_url

signed_url = sign_url(
    auth_user='user', 
    secret_key='your-secret_key', 
    url='http://e.com/api/'
)
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'
)