【问题标题】:Are these random numbers 'safe'这些随机数“安全”吗
【发布时间】:2009-07-06 07:58:08
【问题描述】:

我不是在问这些是否真的是随机的。我只是想知道如果两个用户同时点击一个页面,他们能得到相同的随机数吗?我在想,如果我在多核服务器上运行它,由于同步或其他原因,我会在很长一段时间内生成相同的随机数吗?

public static class SBackend
{
    static Random randObj = null;
    public static void init()
    {
        randObj = new Random((int)DateTime.Now.ToBinary());
        runFirstTime();
    }

    public static long getRandomId()
    {
        long randNum = (long)randObj.Next() << 33;
        randNum |= (uint)randObj.Next() << 2;
        randNum |= (uint)randObj.Next() & 3;
        return randNum;
    }
}

【问题讨论】:

  • 嗨 AcidZombie,为什么你需要保证两个人不会得到相同的随机数?您要解决的根本问题是什么?你也在 ASP.Net 中吗? (当您提到问题中的页面时)
  • 我将它用于两件事,但我意识到我可以将线性值用于其中之一。我目前正在使用它来生成一个 logid Id。用户有一个带有用户名的 cookie 和一个带有 loginId 的 httpcookie。如果任何人都可以猜到用户拥有的特定 loginId,他们就可以以他们的身份登录。请注意,每次用户登录时都会更改此设置并在注销时设置为 0

标签: c# random security


【解决方案1】:

是的,它可以生成相同的数字。种子不添加任何内容(默认情况下它是基于时间的)。

另外 - 如果它是静态的,你应该同步它(Next 不是线程安全的):

static readonly Random rand = new Random();
public static int NextInt32() {
    lock(rand) { return rand.Next();}
}
public static long NextInt64() {
    lock(rand) { // using your algorithm...
        long randNum = (long)rand.Next() << 33;
        randNum |= (uint)rand.Next() << 2;
        randNum |= (uint)rand.Next() & 3;
        return randNum;
    }
}

这当然可以仍然巧合地生成相同的数字...

如果熵很重要,也许可以考虑使用加密随机数生成器。

【讨论】:

  • 即使使用 CSPRNG,您也可以获得相同的数字 :)
【解决方案2】:

好吧,如果你只用它来生成不一样的随机数,为什么不使用 System.Guid.NewGuid()?

【讨论】:

  • “永远不一样” - 好吧,Guid 不太可能是一样的;但不能保证。
  • 好吧,无论如何,使用 RNG 是不可能的。
【解决方案3】:

您可以使用锁定机制来确保同时只有一个线程可以访问 Random 对象。

 public static class ThreadSafeRandom
{
    private static Random r = new Random();
    public static double NextDouble()
    {
        lock (r)
        {
            return r.NextDouble();
        }
    }

    public static int Next(int min, int max)
    {
        lock (r)
        {
            return r.Next(min, max);
        }
    }
}

【讨论】:

    【解决方案4】:

    您可以在同步函数中使用linear congruential generator 来生成伪随机数,这些伪随机数只会在每个m 请求中重复(请参阅文章了解如何编写)。将 m 设为 64 位值,您就不太可能收到足够多的请求来重复自己,更不用说为两个并发访问者提供相同的号码了。

    【讨论】:

    • 另外,在线程环境中,你可以为N个worker中的每一个制作一个生成器,并且每次将它们步进N步,因此worker 1使用X1,xN+1,x2N+1, ...,worker 2 使用 x2, xN+2, x2N+2.... 并且worker之间没有冲突,也不需要同步。
    【解决方案5】:

    如果您需要会话编号,请使用 GUID。

    【讨论】:

      【解决方案6】:

      伪随机数生成器 (PRNG) 仅与它们拥有的种子值一样好。现在我看到您已将它们定义为静态类和静态方法,因此假设您已采取适当的安全措施以确保 getRandomId() 不会被多个线程同时调用,那么您应该处于正常状态。

      但是,锁定对 getRandomId 的访问会引入瓶颈,并且不会随着流量的增加而无限扩展。

      【讨论】:

        【解决方案7】:

        当您使用基本的 Random 类时,总是有可能生成相同的数字。我更好的方法是使用System.Security.Cryptography.RandomNumberGenerator,这将创建加密强随机值。

        【讨论】:

          【解决方案8】:

          正如其他人所说,是的,如果两个线程在同一个刻度内请求随机值,你可以让两个随机值相同。

          这是另一个线程安全的随机实现。这个每个请求使用的锁更少,因此速度更快。

          public static class ThreadSafeRandom
          {
              private static Random _global = new Random();
              [ThreadStatic]
              private static Random _local;
          
              public static int Next()
              {
                  Random inst = _local;
                  if (inst == null)
                  {
                      int seed;
                      lock (_global) seed = _global.Next();
                      _local = inst = new Random(seed);
                  }
                  return inst.Next();
              }
          }
          

          请参阅 Parallels 团队的 "Getting random numbers in a thread-safe way" 了解更多信息。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2021-02-20
            • 2011-08-14
            • 1970-01-01
            • 2019-08-09
            • 1970-01-01
            • 1970-01-01
            • 2011-02-15
            相关资源
            最近更新 更多