2

I need to create a unique hash that corresponds to an ID in the database. Should I just generate a random alphanumeric hash that I then store in that particular row in the database?

Right now this is what I'm doing but I need to make sure people can't figure out the algorithm that creates these hashes.

params[:slug].to_i(36)

id.to_s(36)
the Tin Man
  • 158,662
  • 42
  • 215
  • 303
Tyler
  • 2,346
  • 6
  • 33
  • 59
  • You may want to check this one out: http://stackoverflow.com/questions/6338870/how-to-implement-a-short-url-like-urls-in-twitter – Casper Dec 20 '14 at 21:31
  • Databases excel at generating UID values, so check your DBM's documentation to see what it supports. It'd be faster to let the DBM do it than Rails. – the Tin Man Dec 20 '14 at 21:34

3 Answers3

8

Update Oct 2020:

Don't try to be clever. A UUID is more than enough, they're cheap and fast to generate, forget about collisions, other failures are way (way WAY!!) more likely. The idea behind UUIDs, interesting stuff.

require 'securerandom'
SecureRandom.uuid
#=> "a62f45ba-292d-425b-fb49-3733b00defe1"

Old answer:

Very simple actually:

enum = [*'a'..'z', *'A'..'Z', *0..9].shuffle.permutation
enum.next.join
 => "qmrbSTBu6gGpMs4Jh0VZAiI9cW58jxoDz2NwL1eUClaFtdRXfPEOYQnvkKy7H3"

This provides factorial(62) uniq strings/uids. (A very large number!)

You can also provide a limit to permutation if you want shorter strings/uids, however this will decrease the amount of uniq strings/uids you can generate.

enum = [*'a'..'z', *'A'..'Z', *0..9].shuffle.permutation(13)
enum.next.join
 => "A1BD3qljTKpOm"

If you're concern about security, then, shuffle the array with a secure random seed:

ary = [*'a'..'z', *'A'..'Z', *0..9].shuffle(random: SecureRandom.hex(23).to_i(16))
enum = ary.permutation(13)
enum.next.join
 => "9bNmv82ruBKjq"

Uniqueness is guarantee (thus limited), without the overhead of calling a database or testing uniqueness.

yeyo
  • 2,954
  • 2
  • 29
  • 40
  • @TylerRice Could you elaborate more please, decode what exactly? No one will be able to guess the next sequence if that's what you mean, one will need the random `seed` first before doing any attempt, and because the `seed` is being generated dynamically it will be extremely hard to obtain. – yeyo Dec 21 '14 at 02:36
  • oh ok... I understand what this does now.... I was thinking along the line of having an integer id and turning that into a hash and then being able to decode it back to the integer id – Tyler Dec 21 '14 at 03:34
  • @TylerRice, yes!, you can apply a hash function to the newly generated string and then assign an `id` to the result of that function (SHA1, SHA256, SHA512). One thing though, do not use a RDMS for this task, instead consider using Redis or Memcached to store this information, they serve for this very same purpose. – yeyo Dec 21 '14 at 04:01
  • @TylerRice do not use the hash of the id numbers, they could be easily cracked in places like this one: https://crackstation.net/. This web page also tells how to properly salt the input of a hash function, here: https://crackstation.net/hashing-security.htm. Good reading. – yeyo Dec 21 '14 at 04:07
  • @TylerRice yeah, that's one of those bookmarks one plan to read in the future, but end up forgetting about it existence. – yeyo Dec 21 '14 at 13:06
3

use the SecureRandom library. It does not guarantee unique values so you need to do a Model.exists?(field_name: new_id) before assigning the new_id.

   def generate_random_id
      loop do
        random_number = SecureRandom.hex(10)
        break random_number unless User.exists?(random_id: random_number)
      end  
    end

http://ruby-doc.org/stdlib-2.1.2/libdoc/securerandom/rdoc/SecureRandom.html

does this answer your question?

Aditya Shedge
  • 386
  • 1
  • 7
  • does this allow for alphanumeric characters or just numeric? I really need to make this as short as possible for the urls because I will be sending text messages with these urls – Tyler Dec 20 '14 at 21:24
  • please go though the documentation. I am not sure if this is the solution you may be looking for. I don't know your exact requirement. http://ruby-doc.org/stdlib-2.1.2/libdoc/securerandom/rdoc/SecureRandom.html – Aditya Shedge Dec 20 '14 at 21:44
  • thanks for sharing that ... it will definitely work... I just won't be able to decode which is fine I was just trying to be lazy lol – Tyler Dec 20 '14 at 21:49
1

Simply if you want to use characters then,

('a'..'z').to_a.shuffle[0..7].join

you can change the length as you would need.

('a'..'z').to_a.shuffle
=> ["c", "g", "l", "k", "h", "z", "s", "i", "n", "d", "y", "u", "t", "j", "q",
"b", "r", "o", "f", "e", "w", "v", "m", "a", "x", "p"]