11

我的最终目标是创建一个唯一且无法猜测/预测的 URL。此 URL 的目的是允许用户执行验证其电子邮件地址和重置密码等操作。这些 URL 将在设定的时间(当前设置为 24 小时)内过期。

我最初是Guid为此目的使用 a 的,但我现在理解这介于“还好”和“非常不安全”之间,具体取决于您听哪位专家。所以,我想我会稍微加强一下我的代码,以防万一。起初我以为我会坚持使用 a Guid,但从随机字节而不是Guid.NewGuid()工厂方法生成它。这是我想出的方法:

public static Guid GetRandomGuid()
{
    var bytes = new byte[16];
    var generator = new RNGCryptoServiceProvider();
    generator.GetBytes(bytes);
    return new Guid(bytes);
}

我不太清楚当你使用new Guid(bytes)instead of时究竟会发生什么Guid.NewGuid(),但我认为这个方法所做的只是生成一个随机字节数组并将其存储Guid数据结构中。换句话说,它不再保证是唯一的,它只保证是随机的。

由于我的 URL 需要是唯一的随机的,这似乎不是一个可靠的解决方案。我现在在想,我应该将我的 URL 建立在一个唯一 ID(可以是一个Guid,或者,如果有的话,一个数据库自动递增的 id)和从RNGCryptoServiceProvider.

问题

生成既保证唯一又极难/不可能预测/猜测的验证/密码重置 URL 的最佳方法是什么?

  • 我是否应该通过将唯一字符串与随机字符串连接来简单地构造我的 URL?

  • .NET Framework 是否有一种内置方法可以轻松生成可用于 URL的唯一且随机(不可预测的)序列?

  • 如果没有,是否有一个好的开源解决方案?

更新

如果有人有类似的要求,我目前正在使用以下方法:

public static string GenerateUniqueRandomToken(int uniqueId)
    // generates a unique, random, and alphanumeric token
{
    const string availableChars =
        "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
    using (var generator = new RNGCryptoServiceProvider())
    {
        var bytes = new byte[16];
        generator.GetBytes(bytes);
        var chars = bytes
            .Select(b => availableChars[b % availableChars.Length]);
        var token = new string(chars.ToArray());
        return uniqueId + token;
    }
}

如果您对此有任何问题,请发表评论。

4

5 回答 5

6

Guid 并不意味着是一项安全功能。创建 Guid 的标准没有声明它的安全性,或者猜测的难易程度。它只是声称它的独特性。

如果您尝试使用 Guid 来保护您的系统,那么您将没有一个安全的系统。

于 2012-09-03T01:08:17.267 回答
0

您可以通过使用随机密钥键入的 AES 计数器运行计数器来生成 128 位“随机”唯一数字。只要使用相同的键,就不会重复任何输出。

static byte[] AESCounter(byte[] key, ulong counter) {
    byte[] InputBlock = new byte[16];
    InputBlock[0] = (byte)(counter & 0xffL);
    InputBlock[1] = (byte)((counter & 0xff00L) >> 8);
    InputBlock[2] = (byte)((counter & 0xff0000L) >> 16);
    InputBlock[3] = (byte)((counter & 0xff000000L) >> 24);
    InputBlock[4] = (byte)((counter & 0xff00000000L) >> 32);
    InputBlock[5] = (byte)((counter & 0xff0000000000L) >> 40);
    InputBlock[6] = (byte)((counter & 0xff000000000000L) >> 48);
    InputBlock[7] = (byte)((counter & 0xff00000000000000L) >> 54);
    using (AesCryptoServiceProvider AES = new AesCryptoServiceProvider())
    {
        AES.Key = key;
        AES.Mode = CipherMode.ECB;
        AES.Padding = PaddingMode.None;
        using (ICryptoTransform Encryptor = AES.CreateEncryptor())
        {
            return Encryptor.TransformFinalBlock(InputBlock, 0, 16);
        }
    }
}
于 2012-09-05T05:46:44.493 回答
0

获取所有用户登录凭据的 md5 并将其与 Guid.NewGuid().ToString() 生成的 guid 连接并在您的 url 中使用它,它应该可以正常工作。

于 2013-04-21T20:01:58.300 回答
0

[编辑:我错过了上面对 RNGCryptoServiceProvider 的调用。对此表示歉意。]

对于您的案例,RNG 生成器(如 RNGCryptoServiceProvider)的问题在于它们不能保证唯一性。如您所知,Guid 在统计上不太可能是唯一的,但不能保证它是唯一的。保证唯一性和随机性的唯一真正方法是生成 GUID,就像您将它放在可搜索的存储中一样,例如数据库表。每当您生成一个新值时,请检查它是否已经存在。如果是,则丢弃它并生成一个新值。

于 2012-09-03T03:26:26.987 回答
0

在数据库中创建一个带有 linkID 和 Datesent 列的表,在生成链接时发送插入DateTime.Now到表中并返回 linkId,将 linkID 设置为activationLink 上的查询字符串参数。

在加载激活页面时检索linkId并使用它来调用一个存储过程,该过程将在将相应的linkId作为参数传递时返回日期,当您获取日期时,您可以添加您希望链接保持活动多长时间使用.AddDays()/ .AddMonths(这是日期时间的 C# 方法)。然后将您返回的日期与今天的日期进行比较。如果它已经过了几天或几个月的长度,则给出错误消息,否则继续并显示页面内容。

我建议您将页面内容保留在面板中并设置它,visible = "false"然后仅visible="true"在日期仍在范围内时才制作面板。

于 2015-03-05T17:34:55.620 回答