25

I always wanted to use randomly generated strings for my resources' IDs, so I could have shorter URLs like this: /user/4jz0k1

But I never did, because I was worried about the random string generation creating actual words, eg: /user/f*cker. This brings two problems: it might be confusing or even offensive for users, and it could mess with the SEO too.

Then I thought all I had to do was to set up a fixed pattern like adding a number every 2 letters. I was very happy with my 'generate_safe_uuid' method, but then I realized it was only better for SEO, and worse for users, because it increased the ratio of actual words being generated, eg: /user/g4yd1ck5

Now I'm thinking I could create a method 'replace_numbers_with_letters', and check that it haven't formed any words against a dictionary or something.

Any other ideas?

ps. As I write this, I also realized that checking for words in more than one language (eg: english and french, spanish, etc) would be a mess, and I'm starting to love numbers-only IDs again.

UPDATE

Some links everyone should read:

http://thedailywtf.com/Articles/The-Automated-Curse-Generator.aspx

Link

10
  • Use a hash or checksum? If you prefer to use a random string, theres no rule that you have to use every letter in the alphabet. Apr 7, 2012 at 16:42
  • 25
    Don't call it a uuid, uuid are universally unique identifiers. Its refers to a specific system of identifiers that you can use. That's not what you are doing here, so don't use that term. Apr 7, 2012 at 17:56
  • 4
    I will just leave you with the tale of the Automatic Curse Generator Apr 7, 2012 at 20:25
  • 1
    @HappyDeveloper, firstly, its not universal. It is specific to your application. Secondly, uuid specifically refers to en.wikipedia.org/wiki/Universally_unique_identifier not any similar scheme that you devise. Apr 7, 2012 at 22:36
  • 2
    This is such an incredible waste of braintime. The chances of it actually happening is way too small to be worth even thinking about... Apr 8, 2012 at 19:55

7 Answers 7

6

Consider using a numeric or hexadecimal key instead. It will save you a lot of trouble compared to writing an i18n-aware profanity filter, and the worst you'll have to worry about is dead beef.

2
  • 1
    +1: I think this is the simplest and safest solution. You can generated a uuid in the form of a number and use a string representation for it (decimal, hexadecimal, octal).
    – Giorgio
    Jun 26, 2012 at 19:58
  • 7
    You still have to worry about B16B00B5 :P Jun 4, 2013 at 21:51
6

A couple of tips that will lower the chances of inadvertently creating meaningful words:

  • Add some non-alpha, non-numerical characters to the mix, such as "-", "!" or "_".
  • Compose your UUIDs by accumulating sequences of characters (rather than single characters) that are unlikely to occur in real words, such as "zx" or "aa".

This is some C# sample code (using .NET 4):

private string MakeRandomString()  
{  
    var bits = new List<string>()  
    {  
            "a",  
            "b",  
            "c",  
            "d",  
            "e",  
            //keep going with letters.  
            "0",  
            "1",  
            "2",  
            "3",  
            //keep going with numbers.  
            "-",  
            "!",  
            "_",  
            //add some more non-alpha, non-numeric characters.  
            "zx",  
            "aa",  
            "kq",  
            "jr",  
            "yq",  
            //add some more odd combinations to the mix.  
    };  

    StringBuilder sb = new StringBuilder();  
    Random r = new Random();  
    for (int i = 0; i < 8; i++)  
    {  
        sb.Append(bits[r.Next(bits.Count)]);  
    }  

    return sb.ToString();  
}  

This doesn't guarantee that you won't offend anyone, but I agree with @DeadMG that you cannot aim so high.

7
  • 1
    problem with non-alphanumeric is that some of them shall not play nice in URIs (leading to escaped character, which are a big no-no in a tiny URL: there's a reason with bit.ly and tinyurl aren't using them). The other issue is that they're less intuitive to user: they aren't easy to, say, write down on a post-it or transmit on the phone (many non-techies have no clue what the name of underscore is, for example). Once again there's a reason why tiny url and bit.ly aren't using them.
    – user988052
    Apr 8, 2012 at 0:14
  • @user988052: Hence some non-alpha, non-numerical characters. It's easy to select a few that are fine for URIs and easy enough for humans.
    – CesarGon
    Apr 8, 2012 at 21:05
  • "Hence some non-alpha, non-numerical characters." [sic]... URL shortening services (bit.ly, tinyurl, t.co, goo.gl, etc.) seems to think that zero non-alphanum is better than "some". And I think that the reasons I explained in my previous comments are part of the explanation as to why these services aren't agreeing with your point of view. Now obviously our opinions are differing on the matter and I'll leave you the last word ; )
    – user988052
    Apr 8, 2012 at 21:59
  • @user988052: I've been using goo.gl for ages and it never had an issue with converting all sorts of non-alpha characters; the only exception being %. You can find this documented in the service's discussion group. Can you provide any reference that backs up your claims?
    – CesarGon
    Apr 9, 2012 at 7:58
  • 1
    OP stated he wanted short names and asks for a method to generate these. You suggest "adding son non-alpha, non-numerical characters to the mix" [sic]. So what are you suggesting? That OP first generates "something" and then send this to tinyurl/bit.ly? I think that is not what OP is after. OP wants to directly generate a URL that is relatively "tiny". All I'm saying is that if that's what he's after, then it may be better for him to use an alphanum alphabet, just like tinyurl/bit.ly is doing! Now I'm really off.
    – user988052
    Apr 10, 2012 at 11:03
5

Just create a naughty word list, a letter substitution list, and then if any ID generated is a naughty word, redo it.

For instance (pseudo code)

naughty_words = ["ass", "shit", "boobs"]
substitutions = {
    "4" : "a"
    "1" : "i"
    "3" : "e"
    "7" : "t"
    "5" : "s"
    "0" : "o"
    // etc.
}

function reducestring (str) {
    newstr = ""
    for (character in str) {
        if (substitituions[character]) newstr += substitutions[character]
        else newstr += character
    }
    return tolower(newstr)
}

do {
    new_id_numeric = random_number()
    short_id = compress_to_alphanumeric(new_id_numeric) // 0-9, a-z, A-Z
    // that function should create a base 62 number
} while (!contains(naughty_words, reducestring(short_id))

(You can refer to other short url recommendations like this one for info on base 62 hashing/conversion)

Now you no longer get IDs like a55, sh1t, or "b00bs". Your letter substitution list would only need to contain characters in your naughty words, obviously.

Since no one is going to read "455" as "ass" then you might also want to return str in reducestring if it doesn't contain any letters.

Examples

The graphic-design site Dribbble has its own short string ids for posts. These use 0-9, a-z and A-Z like http://drbl.in/dCWi.

I did some experimenting and there are short ids for at least a few naughty words. I guess we'll see when they get to f, but they aren't there yet.

Granted -- giving a user their own personally-identifying url (/user/whatever) instead of just a post is much worse with naughty words.

6
  • 2
    I once wrote a program that generated passwords for an online service. They were random, but there were a few heuristics that made them sorta pronounceable, so they would be more easily remembered. And these heuristics led to profanity. The solution was as described here: check for vulgar substrings, including those that could be pronounced similarly to vulgar words (e.g. look for FUC and FUK) and regenerate the password. (For giggles, the program wrote the rejected passwords to a separate file.)
    – kindall
    Apr 8, 2012 at 2:10
  • 1
    And how on earth are you going to write such a thing for every language?
    – DeadMG
    Apr 8, 2012 at 11:20
  • 1
    @DeadMG For the full set of all possible offensive words, this can only make that set smaller. Is your stance really: "because you can't reach 100%, it's automatically not worth doing anything"?
    – Nicole
    Apr 8, 2012 at 15:00
  • What about UTF-8? There's lots of alternative printable chars that get around this substitution. Apr 8, 2012 at 19:44
  • 1
    @JBRWilkinson that doesn't apply because the OP is setting the character set of alphanumeric characters for IDs, right?
    – Nicole
    Apr 8, 2012 at 20:08
3

There are essentially two strategies that you can employ:

  1. Create a system that won't generate any offensive strings. For example, you can compose your id's only from consonant letters. By leaving out all vowels, you can be sure that your system will never generate any English words, naughty or otherwise.

  2. After generating a completely random id, check to make sure that the new id doesn't include any offensive substrings.

2
  • The first idea is a great one; it barely reduces the number of values per character-slot, while solving 90+% of cases! For UUIDs, switching from base64 to this base54 only increases the string length by 1 character ((54^23) - (64^22) > 0). Anyway, it's what I plan to use now. Kudos sir. :)
    – Venryx
    Jun 10, 2020 at 10:08
  • People who are extra cautious may also want to exclude the four numbers which are commonly interpreted as vowel equivalents: 4(A), 3(E), 1(I), 0(O) [as per: gamehouse.com/blog/leet-speak-cheat-sheet]
    – Venryx
    Jun 10, 2020 at 11:04
3

You can never prevent an automated system from generating some string that's offensive to a user. For example, in China some numbers are considered unlucky.

All you can really do is tell the user that their ID is random and the contents are irrelevant and if they get /user/fucker then they should just ignore it. These things happen and it's just not technically feasible to avoid it- just like you can never filter profanity.

5
  • 10
    I am not the downvoter, but I feel very strongly that for offensive words you really need to do a much, much, much better than "tell them they should just ignore it". The least you could do is offer some way to change the generated id to one they do find acceptable. Apr 7, 2012 at 16:48
  • 5
    I'm not the downvoter either, but I agree with @MarjanVenema, /user/f*cker is not acceptable Apr 7, 2012 at 16:52
  • @HappyDeveloper: As I previously suggested, what are you going to do about it? You can't prevent users from being given IDs that they find offensive.
    – DeadMG
    Apr 7, 2012 at 21:58
  • 3
    @DeadMG You can help the situation by preventing a few commonly-offensive cases. I thought the original question made that pretty clear.
    – Nicole
    Apr 7, 2012 at 23:06
  • 2
    @NickC: The only examples are commonly offensive in English. Do you have any idea what's commonly offensive in Arabic, Portuguese, Chinese, Russian? Not to mention the fact that those languages may have swearwords that take many, many forms. It's easy to special-case the obvious forms of the words from English, but not so easy to do it for everybody.
    – DeadMG
    Apr 8, 2012 at 11:14
1

In many situations (email spam, ip blocking, etc), a blacklist is a losing game -- you'll never be able to make a "complete" blacklist of every possible bad thing that could ever occur. a b c d e f

Many people use a whitelist of acceptable words and string them together in some random order. (Perhaps with a dash or dot or space between each word).

Some popular dictionaries that are used for converting arbitrary numbers to a pronounceable series of words include:

0

You can either make it just randomly generated numbers, or have a regex to cancel out the ones that are offensive:

/ass/ =~ userid
/boobs/ =~ userid
/morenaughtywordshere/ =~ userid
2

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.