这种已签名URL的实现是否合理安全



我正在尝试实现签名URL,以实现对静态文件的短期访问。想法是:

  • 生成带有过期时间戳的URL(例如https://example.com/file.png?download=false&expires=1586852158(
  • 使用HMACSHA256和共享密钥对其进行签名,并将签名附加在URL的末尾(例如https://example.com/file.png?download=false&expires=1586852158&signature=6635ea14baeeaaffe71333cf6c7fa1f0af9f6cd1a17abb4e75ca275dec5906d1

当我在服务器上收到请求时,我取出signature参数,并验证用HMACSHA256和相同共享密钥签名的URL的其余部分是否会产生相同的签名。

实现如下:

public static class URLSigner
{
private static string GetSignatureForUri(string uri, byte[] key)
{
var hmac = new HMACSHA256(key);
var signature = hmac.ComputeHash(Encoding.UTF8.GetBytes(uri));
var hexSignature = BitConverter.ToString(signature).Replace("-", string.Empty).ToLowerInvariant();
return hexSignature;
}
public static string SignUri(string uri, byte[] key)
{
var hexSignature = GetSignatureForUri(uri, key);
return QueryHelpers.AddQueryString(uri, new Dictionary<string, string> { { "signature", hexSignature }});
}
public static bool VerifyUri(string uri, byte[] key)
{
var signatureRegex = "[\?&]signature=([a-z0-9]+)$";
var signatureMatch = Regex.Match(uri, signatureRegex);
if (!signatureMatch.Success || signatureMatch.Groups.Count != 2)
return false;
var parsedSignature = signatureMatch.Groups[1].Value;
var originalUri = Regex.Replace(uri, signatureRegex, "");
var hexSignature = GetSignatureForUri(originalUri, key);
return hexSignature == parsedSignature;
}
}

它是这样使用的:

var signedUri = URLSigner.SignUri("https://example.com/file.png?download=false", secretKey);
var isVerified = URLSigner.VerifyUri(signedUri, secretKey);

这种已签名URL的实现是否合理安全?

您的实现似乎缺少对过期时间的验证,因此任何一个密钥当前都可以无限期地工作。

除此之外,我认为这种方法总体上没有任何问题。不过,您可能希望在时间戳之外添加一个密钥,以便以某种方式识别用户或请求。

这是一篇关于如何将通用方法用于一次性密码的好文章,这基本上就是您正在做的。

https://www.freecodecamp.org/news/how-time-based-one-time-passwords-work-and-why-you-should-use-them-in-your-app-fdd2b9ed43c3/

是的,只要密钥处理得当,它是安全的。散列应该能够确保数据的完整性(URL中的数据不会被其他人修改(。

也许,一个小的改进是disposeHMACSHA256对象(可能是using(,但这可能与安全性无关。

我有一个问题。您是说要使用HMACSHA256私钥,但在安全术语中,您传递给HMAC的不是私钥,而是共享密钥

如果您必须拥有用于签名和验证身份验证的公钥和私钥,我建议使用RSACryptoServiceProvider。使用RSA,您有两个密钥,公钥和私钥。

您的客户端创建一个私钥并保存它,然后将其公钥提供给服务器。所以只有客户端可以签名,任何拥有公钥的人都可以验证

另一方面,无论您最终使用了什么算法,我都建议将签名添加到授权标头中,而不是添加到查询字符串中。这更常见,您不需要在每个请求中匹配正则表达式。

相关内容

  • 没有找到相关文章

最新更新