ruby-on-railsurlruby-on-rails-4security-by-obscurity

Obfuscation of URLs - rails


I'm working on a project that's left me stumped, hoping someone out there might have some interesting input. I see there are several gems available for obfuscation of urls, but they seem to stop at the slug level instead of the controller level - i.e. www.foo.com/mycontroller/8sZ16lp. I'm looking for a method to produce something along the lines of www.foo.com/8asd31Ud, dropping the controller name. I checked the docs of the obsufacate_id gem, but it doesn't appear to go that far.

To give more background - I'm really hoping to have www.foo.com/mycontroller/15/edit = www.foo.com/95Ali32


Solution

  • As this doesn't match the convention of rails RESTful URL's my guess is you'll likely just need to write your own routes and maybe more. Out of curiosity, how would you envision that the system knows what type of object to load with "95Ali32"? This might be better to build with Sinatra or something that gives you more control over the routing and less convention.

    Here's one possible approach that uses a table with your slugs that maps to type and object id:

    # migration
    create_table :slugs do |t|
      t.string :object_type, :null => false
      t.string :object_id, :null => false
      t.string :slug
      t.timestamps
    end
    
    # models
    class Slugs < ActiveRecord::Base
      belongs_to :object, :polymorhic => true
    end
    
    class AModel < ActiveRecord::Base
      has_one :slug, :as => :owner
    end
    
    # routes.rb
    # note that anything else should go above this because this will catch all other URL's
    get '*slug', to: 'slugs#show'
    
    # controller
    class SlugsController < ApplicationController
      def show
        @object = Slug.where(slug: params[:slug])
        raise ActiveRecord::NotFound.new unless @object
        render @object.kind
      end
    end
    

    You would then need to build views for each type of object. See this related question


    Update

    Here's another idea. How obscure do you need the slug? What if each model has a known code and then the ID is encoded some way and appended to the model code? Then you could do something much simpler in the code using routes that are preconfigured:

    # You could generate this too, or just hard-code them
    prefixes = ['8sZ', '95Ali']
    [:a_model, :another_model].each do |model|
      match "#{prefixes.pop}:id", :controller => model.to_s.underscore.pluralize, :action => :show, :as => model
    end 
    

    This would give you routes like these

    /8sZ1 #=> AModelsController#show(:id => 1)
    /95Ali341 #=> AModelsController#show(:id => 341)
    

    You could take this another level and use friendly_id to generate slugs for the model ID's. Or use UUID's instead of integer ID's (supported by PostgreSQL).