【问题标题】:Unpredictable Unique Identifier不可预测的唯一标识符
【发布时间】:2011-10-11 21:09:31
【问题描述】:

对于我正在开发的应用程序,我需要生成一个会话令牌,该令牌必须具有以下属性:

  • 至少对于应用程序来说应该是唯一的,全局唯一会更好
  • 应该是不可预测的
  • 它不应该太长,因为它必须在 http 请求标头中传递,所以越小越好

我正在考虑使用加密随机数调整 Guid 结构,但这可能有点矫枉过正。有人知道/创建了适合所有这些属性的数据结构吗?

【问题讨论】:

  • 为什么不直接使用 Guid?
  • 常规 guid 有什么问题?
  • 我只能重复 GUID 建议。它广泛且容易获得,随心所欲且独一无二,格式化为文本时只有 32 个字节。为什么需要“确保不可预测性”?我通过默默无闻闻到安全感......
  • @Kel 我想看看你预测我的向导
  • @delnan:GUID 不保证以任何方式随机。 Kel 可能希望确保会话密钥的不可预测性有很多原因。例如:如果我可以预测一个广泛使用的 Web 服务将在未来生成哪些会话密钥,那么我可以开始今天开始研究如何攻击可能正在使用该密钥的会话 明天.

标签: c#


【解决方案1】:

让我在这一点上非常清楚。 所有使用 GUID 的答案都大错特错,在任何情况下您都不应该这样做。

GUID 规范中没有任何内容要求它们攻击者无法预测。如果以块的形式分配 GUID,则允许按顺序排列,允许以非加密强度随机性创建 GUID,并且允许根据有关世界的已知事实(例如您的 MAC 地址和当前时间)创建 GUID。 没有什么要求 GUID 不可预测。

如果您需要不可预测的会话密钥,则使用加密强度随机数生成器生成一个以生成足够多的位。

更一般地说:为工作使用正确的工具,尤其是在涉及影响安全的代码时。 GUID 旨在确保两家公司不会意外地为两个不同的接口提供相同的标识符。如果那是您的应用程序,请为其使用 GUID。 GUID 的发明是为了防止良性实体发生事故,而不是为了保护无辜用户免受坚定的攻击者的攻击。GUID 就像停车标志 - 用于防止意外碰撞,而不是防止坦克攻击。

GUID 并非旨在解决事故预防以外的任何问题,因此在任何情况下都不要使用它们来解决加密问题。使用专为解决您的问题而设计并由世界级专家实施的加密库。

【讨论】:

  • 至少没有敲你的答案,但原始问题中也没有与攻击者有关的任何内容。到目前为止,我想说的是,在上下文的非规范中,单独抛出“不可预测”这个词是不完整的。正如有人问“这安全吗?”,正确的回答是“安全应对什么威胁?”,我认为这里可以采取同样的策略。如果答案是“对于普通用户来说是不可预测的”,那么大多数 GUID 实现根本没有问题。对抗有针对性的攻击?你很准。只是说。
  • 这也取决于所创建的 Guid 的版本。 C# 使用 pinvoke/win32 UuidCreate,它使用版本 4 来创建 Guid,它声明它依赖于随机数。除非我错了,否则随机数是不可预测的。重要的是人们要真正研究微软的实现是如何工作的,而不是陈述不正确的信息,尤其是在堆栈溢出时。 UuidCreate 使用 MAC 地址的谣言四处传播是不正确的。
  • @ErikPhilips:v4 guid 不保证是加密强度随机的,并且某些位根本不是随机的。我的观点是不要使用为独特性而设计的东西作为不可预测性的来源。为设计目的使用工具。
  • @ErikPhilips 我不认为UuidCreate 可以保证创建v4 guid。虽然它目前是这样实施的,但将来可能会改变。如果您阅读文档,您会发现无法保证不可预测性。
  • @ErikPhilips:那仍然没有解决生成会话密钥的问题。 Guid,无论是如何生成的,都是为了防止良性实体之间的冲突,而不是保护会话内容免受攻击者的侵害。 在安全方面,请使用该工具专为手头的任务而设计。 这是明智而安全的做法。
【解决方案2】:

您可以使用加密RandomNumberGenerator 并获取您想要生成合适长度的标识符的任意字节数。

    RandomNumberGenerator rng = RandomNumberGenerator.Create();

    byte[] bytes = new byte[8];
    rng.GetBytes(bytes);

    // produces a string like "4jpKc52ArXU="
    var s = Convert.ToBase64String(bytes);

【讨论】:

  • 我会使用大约 16 字节的哈希值。 8 字节散列的冲突可能在数十亿个条目中出现。但代码本身几乎就是我会使用的。
【解决方案3】:

如何使用what ASP.NET uses 生成唯一、不可预测且安全的会话 ID:

using System;
using System.IO;
using System.Web;
using System.Web.SessionState;

class Program
{
    static void Main()
    {
        var request = new HttpRequest("", "http://localhost", "");
        var response = new HttpResponse(TextWriter.Null);
        var context = new HttpContext(request, response);
        var id = new SessionIDManager().CreateSessionID(context);
        Console.WriteLine(id);
    }
}

【讨论】:

  • 有点奇怪的是,在Reflector中看CreateSessionID,根本没有用到HttpContext参数。我猜这是因为SessionIDManager 实现了ISessionIDManager,这意味着可能还有其他SessionIDManagers 可用?
  • @Jesse C. Slicer,是的,但由于它是接口的一部分,所有实现都将它作为参数。同样在SessionIDManagers 的未来实现中,这可能会改变,因此提供它更安全。
  • 没有分歧。我很想知道最初的理由,仅此而已。
  • 也检查了反射器,看起来实现确实非常简单:它使用 RNGCryptoServiceProvider 生成 15 个字节,然后对其进行编码以获得字符串。
  • @Kel,是的,就是这么简单。如果您不想使用 SessionIDManager,您可以简单地借用它在内部使用的代码。
【解决方案4】:

如果您确实必须具有不可预测性,请生成GUID 并使用 128 位位混合器重新排列位。可能最好使用 64 位位混合器来混合高低部分。而且,是的,有比特混合器可以保证每个唯一输入的唯一输出。

请注意,这并非完全不可预测。如果观看的人知道该值是一个模糊的 GUID,那么他可能会检查连续的值,并且通过一些努力,可能会对您的位混合器进行逆向工程。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2023-01-19
    • 2011-08-25
    • 1970-01-01
    • 1970-01-01
    • 2011-08-17
    • 2012-07-18
    • 1970-01-01
    • 2014-03-05
    相关资源
    最近更新 更多