【问题标题】:Is this implementation of signed URLs reasonably secure?这种签名 URL 的实现是否相当安全?
【发布时间】:2020-04-14 08:21:45
【问题描述】:

我正在尝试实现签名 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 参数并验证 URL 的其余部分是否使用 HMACSHA256 签名并且相同的共享密钥会产生相同的签名。

实现如下:

    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 的实现是否相当安全?

【问题讨论】:

标签: c# security .net-core hmac signing


【解决方案1】:

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

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

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

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

【讨论】:

  • 哦对了,expires参数的验证在request handler中验证。在这里,我只是确保签名正确完成并正确验证。关于识别用户,我目前不需要。只有在对 API 的单独请求中验证了所有授权检查后,才会给出签名链接。感谢您的文章:)
  • 验证日期时间应该是签名过程的一部分,否则您可以只传递当前日期时间,但如果有意义,请使用旧令牌。
  • 好吧,假设 URL 被验证,那么 datetime 被验证是正确的。换句话说,如果您要使用有效的签名 URL 并更改 expires 参数,那么验证将失败,因为签名会更改。我还看到签名和验证生命周期是两个正交的事情。
  • @ShoeDiamente 在大多数/所有 JWT 实现中,时间戳的验证是在令牌的验证步骤中完成的。您对 URI 的令牌验证只是验证时间戳没有更改,而不是时间戳仍然有效。
【解决方案2】:

是的,只要正确处理密钥,它就是安全的。哈希应该能够保证数据的完整性(URL中的数据不被其他人修改)。

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

【讨论】:

    【解决方案3】:

    我有一个担心。您是说您想使用 HMACSHA256私钥,但在安全术语中,您传递给 HMAC 的不是 私钥,这是一个共享的秘密

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

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

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

    【讨论】:

    • 是的,我的术语不好。我的意思是共享秘密。提供这些签名的 URL 是为了提供对静态文件(如 PDF、图像或其他文件)的访问权限。这些文件更常用于 HTML 链接和/或&lt;img&gt; 以及其他不容易支持使用特定授权标头发出请求的标签(在这种情况下,我可能会使用现有的授权机制和不记名令牌)。
    猜你喜欢
    • 1970-01-01
    • 2016-10-30
    • 1970-01-01
    • 2021-03-05
    • 2012-07-26
    • 2013-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多