-1

I have a utility function in which I want to have as part of my URL querystring . I need to be sure that it is always unique

public static string RandomString(int length)
{
   const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdefghijklmnopqrstuvwxyz0123456789";
   var random = new Random();
   return new string(Enumerable.Repeat(chars, length)
     .Select(s => s[random.Next(s.Length)]).ToArray());
}

If say I pass in 14 for the length , This generates hash like

 YS5bwVTjwEBhFp
 sNi6EfU5rUxI2Z
 sQKhqhklw22vb2

If i'm using talking about 20,000 uses a year of this, Is it safe to say that it should always be unique?

Again

http://:mywebsite.com?id=sQKhqhklw22vb2 is how i would use it

  • 8
    Of course it's not safe to assume that. `Random` doesn't store up the results of all previous calls for the last year, identify the calling method via reflection, and say, "Oh, it's Miller again, generating a hash. Make sure he gets some fresh ones!" – 15ee8f99-57ff-4f92-890c-b56153 May 30 '17 at 18:25
  • 5
    `Random` makes no promise of uniqueness. – adv12 May 30 '17 at 18:26
  • 3
    For a Globally Unique ID, use... GUID. – adv12 May 30 '17 at 18:26
  • 3
    Use a Guid converted to string, it will not be unique but the chances to collide are so small that is considered unique. – Gusman May 30 '17 at 18:26
  • In fact, quite seriously, if `Random` didn't repeat sequences for some period of time, it would be *less random*. If you see "4,0,1,0,4,0,1,0" today, you could be sure you wouldn't see it tomorrow. – 15ee8f99-57ff-4f92-890c-b56153 May 30 '17 at 18:35
  • What is the use case for this key? – Lasse V. Karlsen May 30 '17 at 18:39
  • @LasseV.Karlsen for a mobile app to get a text , click on a link and 1. get the unique hash value so i can look up what their real ID is, 2. them not to be able to get what the next value is , so if i had a url with id =505346, i obviously don't want me to click the sms text message and then decide... to try 505347 –  May 30 '17 at 18:43

2 Answers2

3

One way to ensure uniqueness is to save the generated values and validate before using that was never used in the past. An alternative is to pre-generate a large number of values, save them in a table, then pick the next available. Variations on this theme are the only way to ensure uniqueness if the parameter has no meaning (no semantics).

Another way is to add meaning. Instead of YS5bwVTjwEBhFp, sNi6EfU5rUxI2Z etc use 0, 1 etc (an incremental value coming from DB). Of course, you can encode this, eg. by encryption, into a value that has no meaning for user, so the user still see sNi6EfU5rUxI2Z but that is base64 encoded encrypted value of 1 or something like that.

And finally, for all practical uses, just using a cryptographic random string (ie. RNGCryptoServiceProvider.GetBytes) should be more than enough. Not guaranteed unique, but hugely improbable to collide.

Remus Rusanu
  • 288,378
  • 40
  • 442
  • 569
  • How about an operation based on the current timestamp? Like a hash or something? Is that a good idea? – degant May 30 '17 at 18:35
  • So this `RNGCryptoServiceProvider.GetBytes` is much better? Can you show me an exmple of how to use it? –  May 30 '17 at 18:45
  • 1
    @degant Strictly for uniqueness hash of timestamp is probably as unique as anything else. But history is full of successful cryptographic attacks on *predictable* hashes. A random generated string is better. – Remus Rusanu May 30 '17 at 18:46
  • I was reading `Guffa` response to similar question https://stackoverflow.com/questions/16616521/how-good-is-this-method-for-generating-random-numbers –  May 30 '17 at 18:51
  • @JeremyMiller better https://stackoverflow.com/questions/19298801/generating-random-string-using-rngcryptoserviceprovider – Remus Rusanu May 30 '17 at 18:53
  • @RemusRusanu - thoughts on my answer of combining? –  May 30 '17 at 19:25
0

Not sure if your System.Random is that great, but you could combine with a much better Cryptopgraphy scheme. 10 of your characters and 40 of better...

Here it is in Linqpad

example : yours generates h0UcVayzu2 the other better one generates LyR7SUYZ-ZPll36wzGI5kMPamKtyXV_rN0Ax6iZG

Combined is then 50 characters

h0UcVayzu2LyR7SUYZ-ZPll36wzGI5kMPamKtyXV_rN0Ax6iZG

Code:

void Main()
{

    var x = RandomString(10);
    x.Dump();

    var y = Example.GenerateIdentifier(40);
    y.Dump();

    var z = x + y;

    z.Dump();


}


public static string RandomString(int length)
{
  const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ=abcdefghijklmnopqrstuvwxyz0123456789";
  var random = new Random();
  return new string(Enumerable.Repeat(chars, length)
    .Select(s => s[random.Next(s.Length)]).ToArray());
}


public class Example {
  static readonly char[] AvailableCharacters = {
    'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 
    'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 
    'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 
    'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', 
    '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_'
  };

  internal static string GenerateIdentifier(int length) {
    char[] identifier = new char[length];
    byte[] randomData = new byte[length];

    using (RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider()) {
      rng.GetBytes(randomData);
    }

    for (int idx = 0; idx < identifier.Length; idx++) {
      int pos = randomData[idx] % AvailableCharacters.Length;
      identifier[idx] = AvailableCharacters[pos];
    }

    return new string(identifier);
  }
}