GithubHelp home page GithubHelp logo

namick / obfuscate_id Goto Github PK

View Code? Open in Web Editor NEW
347.0 8.0 137.0 133 KB

Make your ActiveRecord ids non-obvious. Mixes up record ids in a simple, reversible hashing algorithm so that it can then automatically revert the hashed number back to the original id for record lookup without having to store a hash or tag in the database.

License: MIT License

Ruby 81.90% JavaScript 1.80% CSS 1.84% HTML 14.46%

obfuscate_id's Introduction

obfuscate_id

Build Status Dependency Status Code Climate

Make your ActiveRecord ids non-obvious

cat with sunglasses

obfuscate_id turns a URL like this:

http://example.com/users/3

into something like:

http://example.com/users/2356513904

Sequential ActiveRecord ids become non-sequential, random looking, numeric ids.

# post 7000
http://example.com/posts/5270192353
# post 7001
http://example.com/posts/7107163820
# post 7002
http://example.com/posts/3296163828

Why would you want this?

If your site is scaling well, you might not want to leak that you are getting 50 new posts a minute.

Or, for new websites, you may not want to give away how few people are signed up.

Every website has a third user, but that third user doesn't have to know he is the third user.

Features

  • Extremely simple. A single line of code in the model turns it on.
  • Transforms normal seqential ids into random-looking ten digit numerical strings.
  • Gently masks resource ids while retaining a cleaner look than using an encrypted hash.
  • No database changes or migrations are needed. The record is still stored in the database with its original id.
  • Fast, no heavy calculation.

Installation

Add the gem to your Gemfile.

gem "obfuscate_id"

Run bundler.

bundle install

Usage

In your model, add a single line.

class Post < ActiveRecord::Base
  obfuscate_id
end

Customization

If you want your obfuscated ids to be different than some other website using the same plugin, you can throw a random number (spin) at obfuscate_id to make it hash out unique ids for your app.

class Post < ActiveRecord::Base
  obfuscate_id :spin => 89238723
end

How it works

obfuscate_id mixes up the ids in a simple, reversable hashing algorithm so that it can then automatically revert the hashed number back to the original id for record lookup without having to store a hash or tag in the database.

Each number from 0 to 9,999,999,999 is paired with one and only one number in that same range. That other number is paired back to the first. This is an example of a minimal perfect hash function. Within a set of ten billion numbers, it simply maps every number to a different 10 digit number, and back again.

Plain record ids are switched to the obfuscated id in the model's to_param method.

ActiveRecord reverses this obfuscated id back to the plain id before building the database query. This means no migrations or changes to the database. Yay!

Limitations

  • This is not security. obfuscate_id was created to lightly mask record id numbers for the casual user. If you need to really secure your database ids (hint, you probably don't), you need to use real encryption like AES.
  • To properly generate obfuscated urls, make sure you trigger the model's to_param method by passing in the whole object rather than just the id; do this: post_path(@post) not this: post_path(@post.id).

Versions

This is tested with Rails 4.2.0. For other versions of Rails, please see the releases.

If you are trying to get it to work with a different version of rails that is not tested, let me know in the issues

Development

To run the tests, first clone the repo and run bundler:

git clone [email protected]:namick/obfuscate_id.git
cd obfuscate_id
bundle install

Run the tests

bundle exec rspec spec

Or have Guard run them continuously

bundle exec guard

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request

obfuscate_id's People

Contributors

arikrak avatar bronson avatar colinyoung avatar daguar avatar h-0-b avatar johanneswuerbach avatar laserlemon avatar namick avatar plindelauf avatar rastasheep avatar rfunduk avatar treble37 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

obfuscate_id's Issues

bignum too big to convert into `long'

I got the following error bignum too big to convert intolong'`

at:

  def swapper_map(index)
    array = (0..9).to_a
    10.times.collect.with_index do |i|
      array.rotate!(index + i ^ spin).pop # array.rotate!(241241319064) generates the error
    end
  end

I'm using Rails 3.2 so I know that could be it, but I thought reporting would be useful. I'll investigate and come back if I have results.

Speed for API

To cut down on the time for HTTPS requests, wouldn't it be better to save the random IDs in the database and index them?

class.find_by() not working

It seems that only find method is working when using obfuscated ids, thus not allowing to respond with nil if object is not found in the database. could you please fix that?

EDIT: Never mind. though I might suggest tweaking find method to accept another parameter "obfuscated" because keeping track of where the ids are normal and where they are scrambled is quite hard.

Unable to find model with obfuscated ID via a has_many / through association

Somewhat related to #34.

Account model has obfuscated ID.

User.rb
has_many :accounts, through: :memberships

AccountsController#show
@account = current_user.accounts.find(params[:id])

Error
Couldn't find Account with 'id'=0364209871 [WHERE "memberships"."user_id" = ?]

Workaround suggested in #34 works fine with direct find i.e. Account.find_by_id(params[:id]) but fails to find the account via association.

Having obfuscate_id in more than one model causes a error.

Environment:
I am running Ruby 1.9.3, Rails 3.2.15 and postgres db

I have to separate models, with different obfuscate_id spin:

class Promotion < ActiveRecord::Base
   obfuscate_id spin: 33549763
end

class Survey < ActiveRecord::Base
  obfuscate_id spin: 73549763
end

I am getting the following error when I have obfuscate_id spin: 12345678 in two or more models:

ActiveRecord::StatementInvalid: PG::Error: ERROR:  value "8867469274" is out of range for type integer
: SELECT  "table_name".* FROM "table_name"  WHERE "table_name"."id" = $1 LIMIT 1

This only happens on the update of the model.

Don't compatible with Rails 4

when bundle log this

Bundler could not find compatible versions for gem "rails":
  In Gemfile:
    obfuscate_id (>= 0) ruby depends on
      rails (~> 3.2.1) ruby

    rails (4.1.7)

Updating model with update_attribute doesn't work

It looks like the id doesn't get deobfuscated before performing update_attributes.

I'm using:

bill = Bill.find(Bill.deobfuscate_id(params[:id]))
bill.update_attributes(bill_params)

and I get

ActiveRecord::StatementInvalid: PG::NumericValueOutOfRange: ERROR: value "4903661204" is out of range for type integer

RecordNotFound error when using ActionMailer's deliver_later method

I'm using ActionMailer to deliver mails on model creation. My controller code looks like this:

if @flaw.save
  render :show, status: :created

  FlawMailer.user_email(@flaw).deliver_later
  FlawMailer.reported_email(@flaw).deliver_later
else
...

I'm using deliver_later as I don't want the HTTP response by mailing concerns. However, this doesn't work and throws an error, apparently it's trying to use the obfuscated id to look up the Flaw model (even though I'm passing it as a parameter:

[ActiveJob] Enqueued ActionMailer::DeliveryJob (Job ID: 61049335-05ca-452e-8161-205b7c4bde72) to Inline(mailers) with arguments: "FlawMailer", "user_email", "deliver_now", gid://app/Flaw/18
[ActiveJob]   Flaw Load (0.3ms)  SELECT  "flaws".* FROM "flaws" WHERE "flaws"."id" = ?  ORDER BY created_at DESC LIMIT 1  [["id", 1928367482]]
Completed 500 Internal Server Error in 1596ms (Views: 26.2ms | ActiveRecord: 3.8ms)

ActiveRecord::RecordNotFound (Couldn't find Flaw with 'id'=1928367482):
  app/controllers/flaws_controller.rb:19:in `create'

When I use deliver_now instead, this problem vanishes. So there seems to be some incompatibilities between this gem and ActionMailer's deliver_later method.

Is this gem still alive?

Im just curious if somebody maintains this gem? ๐Ÿค”

It still have to rails 5 support at least ๐Ÿคทโ€โ™‚๏ธ

Access methods to deobfuscate the ID

In my app I pass the Obfuscated ID to my pagination and want to find Posts using it.

e.g.

before = ObfuscateId.deobfuscate_id(before)
@posts = Post.where('id < ?', before).order('created_at desc').limit(10)

But I get an error undefined method 'deobfuscate_id' for ObfuscateId:Module

nested resources fails

Hi.

resources :users do
  resources :signups do
    resources :meters
  end
end

When I want to create a new meter for a signup, it fails:

ActiveRecord::RecordNotFound in MetersController#edit

Couldn't find Meter with id=2890815067 [WHERE "meters"."signup_id" = 4]

Request

Parameters:

{"user_id"=>"9680417952",
"signup_id"=>"0681924370",
"id"=>"2890815067"}

This occurs when I want to edit an existing meter or delete it.

Can not `lock!`an ActiveRecord instance

Add this to spec/models/post_spec.rb and it will fail:

describe Post do
  describe "Reloading a record" do
  ....
    context "while locking" do
      it "does not throw an error" do
        expect(lambda { subject.lock! }).to_not raise_error
      end
    end

I have a PR #54 that fixes this but also wanted to leave the issue here formally.

Gem not working with Rails 5

When upgrading to Rails 5, bundler works for all my other gems - but when I add obfuscate_id, this is the error I get:

bundle update
Fetching gem metadata from https://rubygems.org/.........
Fetching version metadata from https://rubygems.org/...
Fetching dependency metadata from https://rubygems.org/..
Resolving dependencies.............................................................................................................................................................................................................................................................................................................................................................................................................
Bundler could not find compatible versions for gem "rack":
  In Gemfile:
    rails (= 5.0.0.beta3) ruby depends on
      actioncable (= 5.0.0.beta3) ruby depends on
        actionpack (= 5.0.0.beta3) ruby depends on
          rack (~> 2.x) ruby

    rails (= 5.0.0.beta3) ruby depends on
      actioncable (= 5.0.0.beta3) ruby depends on
        actionpack (= 5.0.0.beta3) ruby depends on
          rack-test (~> 0.6.3) ruby depends on
            rack (>= 1.0) ruby

    sass-rails (>= 0) ruby depends on
      sprockets (< 4.0, >= 2.8) ruby depends on
        rack (< 3, > 1) ruby

    capybara (>= 0) ruby depends on
      rack (>= 1.0.0) ruby

    passenger (>= 0) ruby depends on
      rack (>= 0) ruby

    thin (>= 0) ruby depends on
      rack (~> 1.0) ruby
Bundler could not find compatible versions for gem "rails":
  In Gemfile:
    obfuscate_id (>= 0) ruby depends on
      rails (~> 3.2.1) ruby

    rails (= 5.0.0.beta3) ruby

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

Breaks when upgrading to Rails 4.1

This gem worked fine, but it broke when I upgraded from rails 3.2 to rails 4.1. I installed the gem from Github to get the latest version, but now I get an error when I call find on the obfuscated id. Not sure if I'm doing something wrong.

This code:

 Answer.find("0183759926")

...results in the following SQL

SELECT "answers".* FROM "answers" WHERE "answers"."id" IN (1862, '--- {}
') ORDER BY answers.updated_at DESC

which is obviously broken. What needs to be done to fix it?

ActiveRecord::RecordNotFound: Couldn't find User with an out of range ID

I'm using obfuscate_id on most of my models and everything's been working great so far. But when I try the following line: TransactionQueue.group(:user).sum(:amount) I get the error ActiveRecord::RecordNotFound: Couldn't find User with an out of range ID Both User and TransactionQueue use obfuscated_id

class TransactionQueue < ActiveRecord::Base
  obfuscate_id :spin => 123
  belongs_to :project
  belongs_to :user
  belongs_to :reward_tier
  belongs_to :release
  scope :monthly_transaction_queues, -> { where(type: 'MonthlyTransactionQueue') }
  scope :release_transaction_queues, -> { where(race: 'ReleaseTransactionQueue') }

  # We will need a way to know which subscriptions will subclass the Subscription model
  def self.types
    %w(MonthlyTransactionQueue ReleaseTransactionQueue)
  end

end

Obfuscate an ActiveRecord collection

This is useful for APIs or passing an array of IDs to the view.

I propose adding a class method, something like this:

Post.all.obfuscate_ids
# Returns array of obfuscated IDs

Thoughts?

reload no longer works

Rails no longer passes the options hash through from reload to find, which breaks the mechanism obfuscate_id is using to reload. (Tested on ActiveRecord 4.0.9)

Obfuscate_id's override relies on the :no_obfuscated_id being passed to find, but this no longer gets passed.

def reload(options = nil)
  options = (options || {}).merge(:no_obfuscated_id => true)
  super(options)
end

As a result, calling .reload on an object tries to .find the object and deobfuscates an already deobfuscated ID, causing it to fail.

Bignum too big to convert into `long'

I get this error in the swapper_map/swap method. However, if I supply a custom spin it works just fine.

Since you generate the default spin by its model name I should probably tell you the name to reproduce it. It happened in the "Employee"-Model.

Describe the security limitation?

You pointed one of this limitation
This is not security. obfuscate_id was created to lightly mask record id numbers for the casual user. If you need to really secure your database ids (hint, you probably don't), you need to use real encryption like AES.

So it means the obfuscated ids can be reverse engineered to lookup the exact id in the db? From brute-force?

You also said, using AES would be much better but it isnt recommended. Thats contradicting on its own.

In gist, if the app needs to be secure, what are the measures?

Bug on batch actions with ActiveAdmin

I don't know if this can be considered bug, but when you're using obfuscate_id with active_admin, the batch actions can't find records.

Could someone help me on that? (just telling if is a bug or not... i can make a PR for that)

๐Ÿ‘

Fixture load breaks

Saying that I have a model Product.

Then the fixture load function products(:one) would not load anything because the fixture ID is not generated by obfuscate_id but some CRC32.

Option :no_obfuscated_id doest not fix this problem as products() does not directly go to the find method.

My fix is disable obfuscate_id on Rails.test.

Maybe you have better ideas on this.

Using find() in ApplicationController tries to find by obfuscated ID and breaks.

I have an ApplicationController that all controllers extend.

In this ApplicationController, I try to do:

def current_user
@current_user ||= User.find(session[:user_id]) if session[:user_id]
end

If my user model uses the obfuscate_id, this ApplicationController throws an error on current_user:

Couldn't find User with id=5164061535

Any ideas if this is an issue / fault with the gem or am I doing this wrong?
Thanks.

Problem reloading objects

Hi!

We're trying to use your library within our app. I followed your instructions and added obfuscate_id to my model. Controller / Views seem to work properly but not our test suite. The issue comes while using reloadmethod on any loaded object, giving a lot of errors like ActiveRecord::RecordNotFound: Couldn't find User with id=1084515574

On my console:

u= Model.last

u.id
=> 891

u.reload
=> ActiveRecord::RecordNotFound: Couldn't find Model with id=5084735574

Any ideas?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.