Creating simple unique tokens in Ruby with Hasher



2009/10/12 // Leeds // // Feed



Randomly hashed tokens have thousands of uses. One of them is creating a random secure hash as a handle on a user account:

http://awesomewebapp.com/confirm/843e9d74d716b41517addde515f4e6db940be21d

A random unique hash is created and assigned to your account, allowing you to confirm your identity through a link without disclosing any personal information about who you are. Hashes like this can easily be created by digesting a single string or a string with an additional salt injected into it to ensure the original string is even more indecipherable.

I was seeing quite a lot of duplication in areas of my code where I was constructing hashes based on certain attributes of a particular class. This required me to build up a hash-able string and then pass it through SHA1 to digest it. Although this is only a few lines of code, its nice to bundle this up into a reusable module so you can just do something like this:

  generate_hash "somevalueiwanttohash" # => 1a09a8b3dd4b24d5284ac13705523d1b15b55e64

Thats where Hasher comes in, a simple module you can mix into any Ruby project. Heres the code:

  # hasher.rb
  # git://github.com/joshnesbitt/hasher.git
  
  module Hasher
    require 'digest/sha1'
    SECRET = "ilikebigbuttsandicannotlie"

    def generate_hash(string)
      encrypt string
    end

    protected
    def encrypt(string)
      Digest::SHA1.hexdigest(salt + string)
    end

    def salt
      Digest::SHA1.hexdigest(SECRET + time_stamp + random)
    end

    def time_stamp(time = Time.now)
      time.strftime("%d%Y%l%M").to_s
    end

    def random(size = 10)
      (0...size).map { character_set[ rand(character_set.size) ] }.join
    end

    def character_set
      [('0'..'9'),('A'..'Z')].map { |range| range.to_a }.flatten
    end
  end

So say you want to create a confirmation token for a user before_create, you would use it in an ActiveRecord model like this:

  # app/models/user.rb
  
  class User < ActiveRecord::Base
    include Hasher
    
    # ... other ruby code ...
    
    before_create :generate_confirmation_token
    
    protected
    def generate_confirmation_token
      self.confirmation_token = generate_hash("#{self.id}-#{self.email}")
    end
  
  end

Its not much, but if you end up having more models needing similar functionality it makes sense to package it up into a nice mixin.

Other (possibly related) posts

Comments