views:

83

answers:

7

A client and I were brainstorming a way to generate usernames from numeric user ids (generated by the database). The key requirement is that usernames are unique.

The most obvious solution would be just set the username equal to the user id. But an important requirement is that the usernames are not "obviously sequential"-- so while the user ids might got 4000,4001,4002,4003, the client doesn't want users to see that progression in the user name, even if those numbers will eventually be used.

It would also be desirable for the length of the username to be compact-- that is, the length doesn't grow until the user ids have reached the point where it is necessary for the length to grow.

Any suggestions?

Edit: Most of the answers have assumed that security of the mechanism is important-- it is not. The client just doesn't want it to be obvious who is the "senior" member from the user name.

A primary goal of the usernames is that they be easy to remember, so long hashes aren't so good.

Alphanumerics are okay, but the client has a concern about assigning potentially "bad" usernames. fpq321 would be okay, ass123 would not. These will be the public "handle" of the users.

+1  A: 

Have you considered using GUIDs? The are pretty long, but you won't get an overlap. Alternatively how about a MD5 hash?

lod3n
+1  A: 

If they don't have to be friendly to read, you could use an MD5 or similar hash. That always outputs the same length and "never" has any collisions. I, of course, use the word "never" loosely.

Should work just fine for your needs, though.

Aaron
Throw HMAC on top of MD5 and it's even more "secure." Need to watch out for using MD5 with simple integers. You need a prefix or suffix at minimum. There are quite a few MD5 rainbow tables out there with every 32-bit int. Even a few search engines that specialize in "password recovery" can reverse the MD5 of an int in milliseconds.
Pestilence
+1  A: 

A possible approach is to run a hash function on the user id. You can use sequential probing. For example, if a user id hashes to 300 but that is already in use, you next check 301, then 302 then 303.

RossFabricant
+4  A: 

I tend to opt for random words from a wordlist + a random number. Rinse & repeat if you don't have a unique. Collisions are rare, especially if you use two word lists that are sizable. For instance:

Wordlist A (nouns, for instance)

  • Tango
  • Bravo
  • Alpha

Wordlist B (adjectives or verbs)

  • Massive
  • Glorious
  • Disgusting

So, some examples would be "DisgustingTango1208", "GloriousAlpha1912", "MassiveAlpha15", etc. The advantage to this method is that words are far easier for users to remember. As long as you keep the digits low, they won't pose a problem either, much less than 16+ alpha numeric characters.

I know that this doesn't satisfy the length / growth requirement, but doing that will give insight into the linearity of the sequence (by ranges of 1,000). So, it seems that one would be able to derive the ID#, with 1,000 brute-force attempts.

Pestilence
+1  A: 

You could transform the integer somewhat - re-arranging its bytes - swapping byte 1 for byte 3, byte 2 for byte 4, etc and use that as the username. Though that doesn't necessarily keep it compact (since it really makes bigger numbers out of little numbers).

eg something like:

BYTE b1 = i&0xFF;
BYTE b2 = (i>>8)&0xFF;
BYTE b3 = (i>>16)&0xFF;
BYTE b4 = (i>>24)&0xFF;

int nTransformed = (b2<<24) | (b1<<16) | (b3<<8) | b4;

It keeps them unique but not obviously sequential.

Ruddy
+1  A: 

If you want to keep the usernames as short as the user id, you can do something like this: (it just replaces numbers by [chars] [signs] [other numbers])

<?php
// function generates (always the same) username from an ID
function get_username($id){
    $i=0;
    $return=null;
    // define some strings used to generate the usernames (must be 10 chars long)
    $string1=str_split('abcdefghij');
    $string2=str_split('KLMNOPQRST');
    $string3=str_split('3274190258');
    $string4=str_split('ANYSTRINGY');
    $string5=str_split('TENCHARSLO');
    if($splitted=str_split($id)){ // split username
        foreach($splitted as $i => $char){ // loop trough each number of uses_id
            if($i==0) $return .= $string1[(intval($char))]; // 1st char is picked from $sting1
            elseif($i==1) $return .= $string2[(intval($char))];
            elseif($i==2) $return .= $string3[(intval($char))];
            elseif($i==3) $return .= $string4[(intval($char))];
            else $return .= $string5[(intval($char))]; // from 5 chars, pick from sting 5
        }
    }
    return (!empty($return))?$return:false; // return username or false if argument is empty
}
get_username(45879);
?>
Daan
A: 

The usual solution to this problem is to let the user pick their own username, and then associate that with a userid that's used as a primary key.

Mark Bessey

related questions