Rails Login Reset Session - ruby-on-rails

Is it best practice to call reset_session when a user successfully signs in and to call it again when a user signs out? Are there any side effects/problems to doing this?

The Ruby on Rails Security Guide recommends resetting the session id upon successful authentication to protect against session fixation vulnerabilities. Essentially, session fixation involves an attacker setting your session id (or some other method of being able to know what the id is when you hit the login page), and, upon your successful authentication, the attacker sets a cookie for their own browser using your session id and are subsequently authenticated as you. Resetting the session id upon successful authentication completely mitigates such a vulnerability. Some sample code in your create action might look like:
def create
user = User.find_by_email(params[:email])
if user && user.authenticate(params[:password])
old_values = session.to_hash
reset_session
session.update old_values.except('session_id')
session[:athlete_id] = athlete.id
redirect_to root_url, notice: "Authentication successful!"
else
flash.now.alert = "Invalid credentials"
render "new"
end
end
Note that it's important to duplicate the session before resetting it if there is any data you wish to preserve.
As far as calling reset_session on logout, yes, this is also best practice as well.

That's really going to depend on how you store things in session and how you want the security to operate.
Reset session will drop everything from the user's session, so if they hop back into the login screen and sign back in but still had (for example) a shopping cart stored to their session, you'll clear that which may be undesirable.
If you're not storing any data you feel the users might want to hold on to, I know of no reason clearing the session before processing a login attempt would hurt at all, an on sign outs I recommend it.

I consider it good practice to reset the session when a user logs in. That way, malicious individuals can't sniff out their session cookie before a client's connection is encrypted and still use it after they use the sign in form over https. Try:
temp = session
reset_session
session.reverse_merge!(temp)
This way, session gets the new values generated by reset_session, but any other session variables remain intact.

A lot of the answers here haven't aged well due to the Rails API changing so I'll just leave one here that works as of Rails 5.0 at least.
As others have noted the Rails Security Guide recommends calling reset_session on login to avoid session fixation attacks.
You may want your session cleared on login but if you just want to change the session id and keep everything else (i.e. no side-effects) you can do it like this:
def mitigate_session_fixation
old_values = session.to_hash
reset_session
session.update old_values.except('session_id')
end

Related

Devise: How to use remember_me cookie after user sign out?

I'm working on a Rails 4.2 Application and using devise gem for authentication.
For remember_me feature, devise generates a cookie remember_user_token which gets destroy after sign_out.
Is there a way such that Devise should not destroy remember_user_token ?
I tried to false the below config in the initializer
config.expire_all_remember_me_on_sign_out = false
But it didn't help.
I need that cookie after sign-out such that it will populate the login form.
Please help.
Thanks
Coupling authentication with form pre-filling isn't necessarily a good idea. You can store the login in a cookie upon successful login. You can override the create method in your SessionsController, call super to call Devise::SessionsController#create and pass it a block. The block will be executed after a successful log in and will receive the user as a parameter.
class SessionsController < Devise::SessionsController
def create
super do |user|
cookies[:login] = user.login
end
end
end
Here is the low down on cookie store. First off, everything in a cookie is there permanently once it's set or until the user deletes the cookie manually somehow. This means, that if you set user_id and user_group_id, it's there for good in the cookie until updated or deleted. This is different from a session since the session is like ram on a computer, once the browser is closed, the session closes with it as well as all of it's data.
So, this means that when you log out your user, you need to specify that their cookie empties anything you don't wan't it to have. When your user logs in, you set anything that you want the user to have while they are logged in. So, since the session and cookie are separate things completely, they never interact together unless you choose to make them. So your session will never dump its self into the cookie store unless you make it do that.
Every time your users go to your site, you could have a single handshake that makes sure that the cookie matches the db if necessary. Otherwise, you could have differing data what only gets updated on login or what not and without the handshake, the user would have to keep logging in to make sure they are still valid which defeats the purpose of having a cookie in the first place.
The downside of client side cookie storage is security concerns. Depending on how you use the cookie to store data, a person could hijack somebodies cookie on your site and pretend they are them. This can be avoided by careful design, but just assume that whatever is in your cookie store is fair game to everybody so use it carefully and for only non secret data.
Hope this helps!

How to check if a user just started a new session?

I want to determine if a user just started a new session in my Rails app. I am currently using HTTP referer to define what a new session is. But I am not sure if this is the correct way. The following code is written in Ruby.
if !request.referrer || !request.referrer.start_with?(request.protocol + request.host_with_port)
# If the user came from another site or came here directly, then we regard it as a new session
else
# Not a new session
end
You can store an attribute in the session the first time the user visits. If the attribute is set then you can safely conclude they've visited your website before.
If, however, the attribute is not set then it could mean many things:
the user is visiting your site for the first time
the user may have deleted their cookies, and you can't tell that they're returning
the user may visiting from different browser or on a different device too
This is how you would code it up
if session.has_key?(:visited_before)
# user has visited before
else
# new user
session[:visited_before] = true
end
You should be wary of
where the sessions are being stored and memory limitations, but I wouldn't let this stop you from getting started unless it's a high traffic website.
googlebot and other search engine crawlers, you can detect those in the user agent headers and avoid setting the session.

Sign out specific user with Devise in Rails

I have Devise for user authentication.
I want to sign out a user with a specific id.
in my controller
def exit
#user = User.find(5)
sign_out(#user) # this line here signs out the current_user
end
The sign out command of devise, even though I pass the #user, it signs out the current_user.
How can I select a user from the database and sign him out with the devise commands?
I am assuming this is part of some admin module, where you want to sign out a particular user.
However, this is not easy to solve. Whether or not a user is signed in or not is stored in the session. So to sign out another user, you would have to have access to its session.
Note: afaik the sign_out method only works in the current session, or maybe through warden (do not know warden well enough) it could extend to all sessions this current server has ever touched. However: if you use passenger, or some form of rails server cluster (which is pretty common), afaik this will not work. I would be interested to hear otherwise, with some explanation :) The sign_out uses the given parameter to determine the scope to sign out from in (afaik) the current session.
So what we generally did was add a kind of emergency button to sign out all users: which destroys all sessions. Note this is of course only possible if you use some database or document-store backed session-store.
Alternatively you could open all sessions, and look for the correct session (for your user), and then destroy those sessions.
To read data from a specific session in stored in activerecord, you can write the following:
#session = ActiveRecord::Base.connection.select_all( "SELECT * FROM sessions WHERE session_id = '#{sess_id}'" )
Marshal.load(ActiveSupport::Base64.decode64(#session.data))
There are alternative approaches:
use Timeoutable module, and force a timeout for a user?
if you use Rememberable you could do #user.forget_me, but I am not sure that this actually affects the current session?
from the device api doc http://rubydoc.info/github/plataformatec/devise/master/Devise/Controllers/SignInOut#sign_out-instance_method the sign_out(#user) method should works. Is it possible that the current_user by chance has the id 5?

Implementing a simple sign up/log in system in Rails using cookies

I'm fairly new to Rails and I'm trying to implement a really basic user authentication system. My main problem right now is that I do not have a clue what's a good design for this.
What I have now
My Rails app is not a website; it's a webservice that communicates with an iPhone app. All communication is in JSON.
When a user signs up, the app sends a POST request to /users.json with name and password as JSON in the HTTP body. Rails then saves the user id in a cookie:
cookies.signed[:user_id] = #user.id
When a user logs out, the app sends a POST request to /logout.json. Rails gets the user id from the cookie, updates the database and deletes the cookie.
Later, when the user wants to log in again, the app sends a POST request to /login.json with name and password as JSON in the HTTP body. Rails sets the cookie again.
My question(s)
Is this design RESTful? Because login and logout aren't resources.
Is it secure to use cookies like this? I'm planning to use SSL.
Is there a better way to do this?
Tt's a very basic approach
A more RESTful way:
Create a controller called sessions with a create and destroy action. Throw the login/logout out of your head and start thinking in sessions. A login is just a username/password combination. When you login you create/start a session, when you log out you destroy the session. The login is not affected.
Instead of using cookies.signed[:user_id] you should use session[:user_id]
SSL is a big plus, because the password can't be sniffed through the network.
And there are lots of out-of-the-box authentication systems.
My favourite is Devise:
https://github.com/plataformatec/devise
http://asciicasts.com/episodes/209-introducing-devise
http://railscasts.com/episodes/209-introducing-devise
there is a nice screencast ryan bates did on authentication from scratch
http://railscasts.com/episodes/250-authentication-from-scratch
No, for the reason you stated.
No, you need to pass a second parameter to encrypt the user's id.
Well, you could use a gem, there's a few about.
If you do want to code it yourself, then I'd recommend looking at a tutorial. The one at railstutorial.org is pretty good.
The basic idea is to create a user model with an encrypted password, which uses a salt formed from the current time and password. You then create a sessions controller and make it RESTful, but using cookies instead of another resource. Make sure to encrypt the cookie with the salt as well:
cookies.permanent.signed[:remember_token] = [user.id, user.salt]

Implementation of "Remember me" in a Rails application

My Rails-app has a sign in box with a "remember me" checkbox. Users who check that box should remain logged in even after closing their browser. I'm keeping track of whether users are logged in by storing their id in the user's session.
But sessions are implemented in Rails as session cookies, which are not persistent. I can make them persistent:
class ApplicationController < ActionController::Base
before_filter :update_session_expiration_date
private
def update_session_expiration_date
options = ActionController::Base.session_options
unless options[:session_expires]
options[:session_expires] = 1.year.from_now
end
end
end
But that seems like a hack, which is surprising for such common functionality. Is there any better way?
Edit
Gareth's answer is pretty good, but I would still like an answer from someone familiar with Rails 2 (because of it's unique CookieSessionStore).
You should almost certainly not be extending the session cookie to be long lived.
Although not dealing specifically with rails this article goes to some length to explain 'remember me' best practices.
In summary though you should:
Add an extra column to the user table to accept a large random value
Set a long lived cookie on the client which combines the user id and the random value
When a new session starts, check for the existence of the id/value cookie and authenticate the new user if they match.
The author also recommends invalidating the random value and resetting the cookie at every login. Personally I don't like that as you then can't stay logged into a site on two computers. I would tend to make sure my password changing function also reset the random value thus locking out sessions on other machines.
As a final note, the advice he gives on making certain functions (password change/email change etc) unavailable to auto authenticated sessions is well worth following but rarely seen in the real world.
I have spent a while thinking about this and came to some conclusions. Rails session cookies are tamper-proof by default, so you really don't have to worry about a cookie being modified on the client end.
Here is what I've done:
Session cookie is set to be long-lived (6 months or so)
Inside the session store
An 'expires on' date that is set to login + 24 hours
user id
Authenticated = true so I can allow for anonymous user sesssions (not dangerous because of the cookie tamper protection)
I add a before_filter in the Application Controller that checks the 'expires on' part of the session.
When the user checks the "Remember Me" box, I just set the session[:expireson] date to be login + 2 weeks. No one can steal the cookie and stay logged in forever or masquerade as another user because the rails session cookie is tamper-proof.
I would suggest that you either take a look at the RESTful_Authentication plug in, which has an implementation of this, or just switch your implementation to use the RESTful Authentication_plugin. There is a good explanation about how to use this plug in at Railscasts:
railscasts #67 restful_authentication
Here is a link to the plugin itself
restful_authentication
The restful_authentication plugin has a good implementation of this:
http://agilewebdevelopment.com/plugins/restful_authentication
Note that you don't want to persist their session, just their identity. You'll create a fresh session for them when they return to your site. Generally you just assign a GUID to the user, write that to their cookie, then use it to look them up when they come back. Don't use their login name or user ID for the token as it could easily be guessed and allow crafty visitors to hijack other users' accounts.
This worked like a charm for me:
http://squarewheel.wordpress.com/2007/11/03/session-cookie-expiration-time-in-rails/
Now my CookieStore sessions expire after two weeks, whereby the user must submit their login credentials again in order to be persistently logged-in for another two weeks.
Bascially, it's as simple as:
including one file in vendor/plugins directory
set session expiry value in application controller using just one line
I would go for Devise for a brilliant authentication solution for rails.

Resources