My cookie token is strong enough in order to use that for user authentication purposes? - ruby-on-rails

I am running Ruby on Rails 3 and I would know if the code that I am using in order to set the cookie value for user authentication purposes is strong enough.
In my model I have:
require 'digest'
class User < ActiveRecord::Base
...
def make_cookie_id_salt(string)
secure_hash("#{self.id}--#{string}")
end
def secure_hash(string)
Digest::SHA2.hexdigest(string)
end
end
In my controller I have:
cookies.signed[:current_user_id] = { :value => [#user.id, #user.make_cookie_id_salt(#user.id)], :expires => 15.days.from_now }
Is it strong enough? If no, how I can improve that (make an example!)?

Everything that gets put into cookies is stored as plain text.
If you set a cookie, and then check the cookies in your browser you will notice (in your case the cookie name would be current_user_id) that it is represented by a string of characters like: G8gcm9sbCB5b3VyIG93biBhdXRoIHRvIGt... (Not quite plain text, right? It is actually Base64 encoded, but you can easily read it - require('base64'); Base64.decode64(string)).
Rails stores a special _yourapp_session cookie that is used to check the cookies validity. If for example someone/something was trying to modify it, it would get rejected.
Now in your case it doesn't really matter if you try to hash something in the cookie or not.
It is just used for authentication (to look up a user in the database by his id) and you are not storing any unique secret data (Which you should not place in a cookie anyway, but it would be the only reason to hash something)
Of course someone could steal the cookie of a user (if he used a public computer and hasn't cleared his cache, etc.) and log in, but there's no way to prevent that (No matter what kind of hashing was used to obfsucate it)
In conclusion you should be fine with what you have.

Rather than try to create your own, I suggest using the Authlogic gem. In a few minutes of configuration you get a complete authentication solution, including cookies and much more. If you really want to roll your own, install the Authlogic gem and take a look at how they do it.

Devise is another option. It's extremely configurable, pretty DRY, with exhausting wiki.
For now-days I prefer it over Authlogic.

Related

Migrating sessions from cookie_store to Redis in Rails 3

I need to switch the session store in my Rails 3 app from cookie_store to redis-session-store. There are many reasons for this (security, SSO with other Rails and non-Rails apps). Are there any best practices on how to do it without loosing all current sessions?
What i could imagine is a two steps approach:
Collect all user sessions for N days and store them in the DB or in Redis (update if already stored).
Use stored user sessions to create entries in Redis.
Alternatively, on the fly migration would also be possible. Means read cookies, use secret key to decrypt the session data and store it as a new session in Redis.
I realize this ticket is pretty old, but this may help others. We ended up changing our session store to Redis, but then still looking for the legacy cookie (for a week or two) before no longer respecting them.
There are probably some security concerns to consider before using this strategy - you want to make sure those risks are worth it compared to the cost of having to sign your entire user-base out all at once. With Rails, the cookies are encrypted and can't be tampered with.
Here's what we used:
class SessionsController < Devise::SessionsController
LEGACY_COOKIE_NAME = "_old_session_name".freeze
def new
return if detect_valid_cookie
super
end
private
def detect_valid_legacy_cookie
legacy_cookie = request.cookie_jar.encrypted[LEGACY_COOKIE_NAME].presence || {}
valid_user_id = legacy_cookie['warden.user.user.key'].try(:first).try(:first)
return unless valid_user_id
user = User.find_by(:id => valid_user_id)
return unless user
if sign_in user
request.cookie_jar.delete(LEGACY_COOKIE_NAME)
redirect_to root_path # or whever you want
true
else
false
end
end
end
Stolen from here:
http://www.happybearsoftware.com/almost-protect-yourself-from-cookie-session-store.html (the last two sections)
Basically, use this:
Rails.application.config.action_dispatch.cookies_serializer = :hybrid
Quote follows:
This will cause Rails to accept sessions serialized with Marshal and exchange them for sessions serialized with JSON.
After you're confident that all your users sessions have been converted to JSON, you can roll out another release that flips the config value to :json.
Note: If you're storing complex Ruby objects in the session and need them to be serialized with Marshal, you won't be able to use the JSON serializer.

Should I accept a crypted password with AuthLogic?

Basically as the question asks.
The AuthLogic documentation for verify_password_method states:
The name of the method in your model used to verify the password. This should be an instance method. It should also be prepared to accept a raw password and a crytped password.
I'd like to support this, because it allows me to maintain our system's current masquerading behaviour without storing plain-text passwords like we have been in the past. However, it seems to me like allowing a simple string comparison to the crypted_password in the database is just as bad as storing the regular plain-text passwords.
Am I missing something here, or is there some other way to accept a crypted password in AuthLogic?
For reference, this is how I was going to write the valid_password? method:
def valid_password?(password, check_from_database = nil)
if password == self.crypted_password
true
else
super(password, check_from_database)
end
end
Ok, turns out that there's a much easier way to do this (although it seems horribly undocumented, and didn't turn up with a Google search of how to achieve this).
Authlogic::Session::Base.new(#user, true)
That line allows session creation without checking credentials. Obviously you should be careful with this since it assumes that the user has already identified themselves correctly - for my usage, since there is a check to ensure the current user is the admin user, it's safe.

Steps to create my own authentication system, need some guidance

I want to learn how to create my own authentication system, please provide some guidance if am doing this wrong.
I will create a Module in my /lib folder /lib/auth.rb
I will require this module in my ApplicationController.
when a user enters their email + password, I will call a method that will do a lookup in the user's table for a user with the same email, I will then compare the passwords. (i'll add encryption with salt later).
If the user entered the correct credentials, I will create a row in the Sessions table, and then write the session GUID to a cookie.
Now whenever I need to check if the user is logged in, or I need the user object, I will check if the cookie exists, if it does, I will lookup the session table for a row with the same guid, if it exists, I will return the session row and then load the User object.
I realize there are many suggestions one can give, but in a nutshell does this sound like a workable solution?
Now to make this usable, I will have to make some helper methods in my ApplicationController right?
How will I access the current_user from within my views?
P.S I know of other authentication systems, I just want to learn how to create my own.
The basic logic you're following is correct. Of course you can always expand on this with features that you need. For instance, you'll need helper methods for things like "logged_in?" and "current_user". Also, you might want to add session expiry, or session retention as a "remember me" feature.
Go for it, you won't learn authentication systems better than building your own then figuring what's wrong with it.
You should really check out the authlogic gem on github.
http://github.com/binarylogic/authlogic
It also has great instructions on how to set up your users.
After Faisal said what I would say, I only give you answer to the last part of your question:
"How will I access the current_user from within my views?"
try something like this:
class User < ...
def self.current=(u)
#current = u
end
def self.current
#current
end
end
In your views (or any part of your code) you can call User.current. Your controller has to assign a validated user to User.current. Your filters can react to "if User.current.nil?" and so on.
If you want to be thread safe, you may use a thread variable instead of #current:
Thread.current[:current_user] = u

How to deal with authentication for a Ruby API wrapper?

I'm working on an API wrapper for Viddler, which will eventually be made public, and I'm trying to figure out the best way to deal with authentication/API keys, specifically with usage within Rails applications in mind.
The easiest way to write the wrapper would be to just have the code create a new client each time, and the developer could store the API key in a constant for future use:
#client = Viddler::Client.new(VIDDLER_API_KEY)
The problem with this is, it's kind of clunky to have to keep creating client objects and passing in the API key. This gets even more complicated when you throw user authentication into the mix.
I'm thinking some sort of solution where I all the the API key to be set in the environment file and then the authentication would be done in a before_filter.
Viddler::Client.api_key = 'abc123'
Viddler::Client.authenticate! 'username', 'password'
Viddler::Client would then store this in a class variable, and you could call Viddler::Client.new without any parameters and make authenticated calls. One thing I'd be concerned about is that this means the developer would have to be sure to clear out the authentication before or after each request, since the class variables would persist between requests.
Any thoughts?
Storing the API key globally would for sure be pretty useful and certainly is the way to go for that kind of information. User authentication on the other hand I think shouldn't be stored globally, never ever, especially for a high level API, because telling your users to "ensure to add an after_filter :reset_viddler_auth" might lead to some unexpected security risks.
# in a config/initializer/*.rb file or something
Viddler::Client.api_key = "abc123"
# in the controller/action/model/wherever
#client = Viddler::Client.new # anonymous
#client.authenticate!("username", "password") # authenticate anon client
#client_auth = Viddler::Client.new("username", "password") # authenticated client
Guess like that you've best of both worlds :) Maybe even provide a way to create a new client with another API key like,
#client_other = Viddler::Client.new("username", "password", :api_key => "xyz890")
So... just my 2 cents.
PS: not sure how up-to-date it is, but there's already a ruby viddler wrapper, just FYI, http://viddler.rubyforge.org/rdoc/

Can't understand sessions in Rails

Please don't bit my for my misunderstanding.
The sessions are very new for me, and i have some problems.
Okay i read many information about sessions and especially rails session. But this don't give me right imagine about sessions.
Did i understand right, when users send request to server (get) -> Server create a new session (and store this some file in hard drive with session id), session id -> is a random generated num? so, server create a new session (and store session on drive) after this server send back answer to client and set session_id in cookies?
Ok, i debug some params and see some results:
debug(session):
{:_csrf_token=>"jeONIfNxFmnpDn/xt6I0icNK1m3EB3CzT9KMntNk7KU=", :session_id=>"06c5628155efaa6446582c491499af6d", "flash"=>{}}
debug(cookies):
{"remember_user_token"=>"1::3GFRFyXb83lffzwPDPQd", "_blog_session"=>"BAh7CDoQX2NzcmZfdG9rZW4iMWplT05JZk54Rm1ucERuL3h0NkkwaWNOSzFtM0VCM0N6VDlLTW50Tms3S1U9Og9zZXNzaW9uX2lkIiUwNmM1NjI4MTU1ZWZhYTY0NDY1ODJjNDkxNDk5YWY2ZCIKZmxhc2hJQzonQWN0aW9uQ29udHJvbGxlcjo6Rmxhc2g6OkZsYXNoSGFzaHsABjoKQHVzZWR7AA==--348c88b594e98f4bf6389d94383134fbe9b03095"}
Okay, i know, what _csrf_token helps to prevent csrf.
session_id -> is id of the session which stored on hard drive (by default)
but what is _blog_session in cookies?
also, remeber_user_token containes my id (1::*) and what about second part, what is it?
Sorry for this stupid questions, i know what i can easy use any nice auth-plugins (authlogic/clearance/devise), but i want to fully understand sessions.
Thank you.
(also sorry for my english, this is not my native language)
remember_user_token is probably set by your authentication plugin, it is encrypted string, which is stored in users table and is used to authenticate him. Details can vary between plugins.
Second part: you are probably using cookie based session store (it is default),
So, _blog_session stores your encrypted session data.
More about cookie based sessions here and here.
The name "_blog_session" is set in config/initializers/session_store.rb
It looks like:
# Your secret key for verifying cookie session data integrity.
# If you change this key, all old sessions will become invalid!
# Make sure the secret is at least 30 characters and all random,
# no regular words or you'll be exposed to dictionary attacks.
ActionController::Base.session = {
:key => '_blogs_session',
:secret => '07fb6f0d41af4ae06aebb1696fcbb5a5398d4a08570744a4cd53ff237020c43a2022b4041d617d95bcf3f5c4601c7e6c1646eecfc157cc200e7dfedd7d7c6813'
}

Resources