Security of Rails authentication with session[:user_id] - ruby-on-rails

I've noticed a lot of Rails authentication tutorials store the user ID in session[:user_id] to remember the user and authenticate them. Assuming there is somewhere in the app that user_ids are exposed publicly (URLs, property on an HTML attribute, etc.), isn't this insecure since I could just edit my session cookie to use someone else's user_id? Am I missing something here?

According to the Rails Security Guide:
"To prevent session hash tampering, a digest is calculated from the session with a server-side secret and inserted into the end of the cookie."
So it looks like the Session can be presumed to be safe from the user tampering with it (assuming our server side secret is kept safe). However, a user still can read anything in the session hash, so we wouldn't want to store sensitive information.

The cookie tends to not contain the user_id, it contains the session key, which is essentially a random, meaning-free string of characters. The session is stored on the server (in the database, or memcached, or a nosql store like redis etc), and the session holds the user id.
So, the session record (serverside only) for a given user might contain this data:
key: asoiuoi09u23uo8789289askho2
user_id: 1234
And the cookie (client side) holds the session key, so the cookie looks like this:
name: somecookiename
site: www.yoursite.com
content: asoiuoi09u23uo8789289askho2
So, to access someone else's session you would need to get hold of their session key. This is by no means impossible (see session-sniffing) but is made much harder by the use of https (which in turn require SSL certs).

Generally, I have the feeling, that :session_id in cookie session is useless and can be omitted, as it is not checked on the server side in most cases. Or I'm wrong?
In most cases :user_id and signing is enough.

Related

How do sessions and cookies work in Rails?

I have been using Devise for a while to handle authentication on my Rails apps, but never really understood how it works. Because Devise also uses the session storage config set on Rails, I'm assuming this is a question on session handling with Rails.
Basically, I'm an auth newbie. I've read a few articles about authentication but most deal with abstracted libraries (they talk about engines, middle ware, etc) that don't make much sense to me. I'm really looking for lower level details.
Here's what I know so far..
I know about cookies and sessions. Cookies are strings stored on client-side which is used to maintain session across multiple HTTP requests.
Here's my basic understanding of authentication (please correct me if I'm wrong):
When user logs in, we send the SSL encrypted request to the server. If the credentials are valid, we save a random string called session id on the database (or any other data store) as the valid session id associated with a user id. This session id changes for each login/logout of the user.
After saving that session id on our data store, we return a response that asks the browser to set a cookie with the session id. This session id along with the user id would then be sent for successive request to the domain until it expires. For each request, our server would check the session id on the headers and verify if that session id is valid for that user id. If it is, then consider that user authenticated.
Here are my questions:
I've read that by default starting from Rails 2, it now uses CookieStore (instead of SessionStore) which generates session hashes with SHA512 (instead of session ids), and all this is stored on a cookie which means multiple user id's can literally have the same session hash and it would just work fine. It seems to me that this is a very dangerous thing, exposing a large number of hashes with a single secret key stored on the server and basing your entire authentication system based on this key. Is there a real world large scale application that uses hashing instead of storing server side session id's?
On the topic of storing active session id's on server side, I've also read that you can switch to use different kinds of session storage for Rails. Based on this, I've heard of systems moving authentication systems out as services and using auth tokens instead. What's an auth token and how does it differ from a session id?
Seems like I can just keep guessing a random string (for both hashing and server side sessions) to grab an existing session. Is there a way to protect against this? Is it normal to use more values stored on a cookie? (such as the username, real name or even another hash for authentication)
I know I'm asking a lot but I believe this will be useful for people like me who do not understand authentication and will be very useful to get a solid foundation about the topic.
I've read that by default starting from Rails 2, it now uses
CookieStore (instead of SessionStore) which generates session hashes
with SHA512 (instead of session ids), and all this is stored on a
cookie which means multiple user id's can literally have the same
session hash and it would just work fine. It seems to me that this is
a very dangerous thing, exposing a large number of hashes with a
single secret key stored on the server and basing your entire
authentication system based on this key.
Yeah, it seems scary at first blush, but I'm not sure what the danger really is. In Rails 4, session data is encrypted using PBKBF2, and then signed with your session secret. This signing helps detect if the contents of the encrypted session have been tampered and the server will reject the session if it detects tampering.
https://cowbell-labs.com/2013-04-10-decrypt-rails-4-session.html
If someone gains access to the session token (which is used to sign the session cookie), you likely have much bigger problems on your hands than end-users attempting to impersonate the wrong user.
Is there a real world large scale application that uses hashing
instead of storing server side session id's?
I honestly don't know the answer to this one offhand, but I suspect that the fact that this is the "default" for Rails means that there are more than a handful of sites out there using cookie session stores.
On the topic of storing active session id's on server side, I've also
read that you can switch to use different kinds of session storage for
Rails. Based on this, I've heard of systems moving authentication
systems out as services and using auth tokens instead. What's an auth
token and how does it differ from a session id?
I'm doing this on a server now - basically a random hash is generated when a user authenticates, and that hash is stored, encrypted and signed, in the cookie. The cookie hash is a key into a server-side datastore (in my case Redis, but it can be in a relational database or memcache or whatever you like), and the actual session data is the stored server-side mapped to that key. This leaves less of your session data in the hands of the client were people could potentially decrypt and analyze it, so it's generally a bit safer.
Seems like I can just keep guessing a random string (for both hashing
and server side sessions) to grab an existing session. Is there a way
to protect against this? Is it normal to use more values stored on a
cookie? (such as the username, real name or even another hash for
authentication)
Yes, you could do that, but it would take a very very long time. You would also need to guess how to sign the newly tampered cookie data so that it'd match what the server expects to see on its side, and it's signed with a pretty large key.
I really don't think there's much alternative for persisting authentication state to using cookies (I suppose HTML5 Local Storage would work if you're feeling exotic and don't care much about legacy browser support).

How does session and cookie work in Rails 4?

As I understand one of the strategies to store sessions is store it in the cookie. There is one thing I don't understand from the docs:
To prevent session hash tampering, a digest is calculated from the
session with a server-side secret and inserted into the end of the
cookie.
What does this mean? How do they prevent that, if I get a cookie from another user, and I use it in my browser, I can't pretend I am the other user? I guess I don't understand what session hash tampering means.
How do they prevent that, if I get a cookie from another user, and I
use it in my browser, I can't pretend I am the other user?
This is called session hijacking, and is covered in http://guides.rubyonrails.org/security.html#session-hijacking. The recommended way to to mitigate this is by "always forcing SSL connection in your application config file", like so:
config.force_ssl = true
The whole http://guides.rubyonrails.org/security.html is definitely worth a read, for more goodness like this.

rails storing password in a session

I have a rails app that makes web api call , the rails app by itself doesn't have any database or userstore. Every api call needs to be sent username and password for each request.
I would like to provide an authentication mechanism for the rails app.
I am planning to do it this way :
Show a login page
Get the username and password
Store the username and password
Perform a manual authentication either via warden.authenticate or authlogic.something ( or may be even that is not required can just check if session has something stored )
And then when user does something I pass the username and password that was stored earlier.
Now my problem is where do I store the password ?
If I use session I cannot use cookie store obviously , I can use session_store = :active_record_store but not sure if its safe , also I don't have any database as of now so why should I create one just for session ?
Is there any other mechanism to store passwords within a session ? (safe way obviously )
Earlier rails had :
MemoryStore
FileStore
But now both seems to be removed. So any other solution ?
Notes from answers :
Storing encrypted passwords won't work since I need the raw password to be sent to server while making api calls.
I have no control over the API , so I cannot change its authentication.
There is no user profile maintenance on rails app. Everything managed by API calls.
I finally thought to implement custom memory store but it seems to throw stackoverflow error. I got the code from https://rails.lighthouseapp.com/projects/8994/tickets/1876-uninitialized-constant-actioncontrollersessionmemorystore
require 'action_dispatch'
module ActionDispatch
module Session
class CustomMemoryStore < ActionDispatch::Session::AbstractStore
GLOBAL_HASH_TABLE = {} #:nodoc:
private
def get_session(env, sid)
sid ||= generate_sid
session = GLOBAL_HASH_TABLE[sid] || {}
session = AbstractStore::SessionHash.new(self, env).merge(session)
[sid, session]
end
def set_session(env, sid, session_data)
GLOBAL_HASH_TABLE[sid] = session_data
return true
end
end
end
end
Steptools3::Application.config.session_store :custom_memory_store, :key => '_some_xyz'
You could try using Redis as a session store. We use rails3-redis-session-store gem. The source can be found here.
It is very easy to setup, and sessions expire automatically, which makes it safe.
Example config:
YourApp::Application.config.session_store :redis_session_store,
:db => 0,
:expire_after => 10.minutes,
:key_prefix => "your_app:session:"
An alternative would be to use dalli, and thus use memcached as the backend.
Hope this helps.
I would recommend taking the next step and setting up a simple database and save a lot of hassle for yourself and the user, what happens when the user wants to return to the site, they will have to re-register.
I find Devise is awesome for this purpose and very simple to integrate.
If there is an issue where you don't want to have a classic database server running you may want to look at MongoDB
The session cookies are encrypted using the session key. Your data should be secure as long as you keep your session key strong (128 char) and safe.
ActionController::Base.session = {
:key => '_foo_bar_session',
:http_only => true,
:secret => 'dldkdke420934indsknknkfsnh318u84e9u49832dfkdsajdsk'
}
If you want to store the authentication details beyond a browser session then you can store them in signed, permanent cookies.
cookies.permanent.signed[:user_credentials] = [login, password]
The signed cookies are accessed like regular cookies:
cookies[:user_credentials]
Make sure you set a strong cookie_verifier_secret in your initializer file.
ActionController::Base.cookie_verifier_secret ='dskjkjfdshfddsfkhkr3898398430943'
Reference
Signed and Permanent cookies in Rails 3
I will try to analysis your choices:
If Server is hacked with CustomMemoryStore
Consider the following scenario:
4 Users A, B, C, D is logged in.
Your server is hacked. The hacker obtains control of server.
D did some operation.
You found that your server is hacked, and repaired your system.
With CustomMemoryStore, the hacker can get passwords of all users. It’s not too hard to inject some logic into running Rails process, or dump the memory and analysis. Storing password in ActiveRecord, MongoDB, Redis has similar problem.
If Server is hacked with CookieStore?
What if the previous scenario occurs and you are using CookieStore?
Let’s review the mechanism of CookieStore:
Server has a secret key to sign & verify session.
Each time when browser sends a request, server decrypts the session, modify the data, sign the session, and send the session to browser in the cookie.
In other words, hacker cannot get the password from the cookie or from the secret key. He needs both cookie and secret key to stole the password.
In this scenario, the passwords of A, B, C are safe. Only D’s password will be stolen by Hacker. You can minimize the damage by repairing the system ASAP.
The Problem of CustomMemoryStore
Besides the security problem, I know you are aware of that CustomMemoryStore is not scalable. However, the problem might be bigger than you think. You will send request to other web services in your controller action, it will block your entire server if the remote service is slow or down. It might be painful even if you have only concurrent 1~10 users.
Even if you decide to run your application on single server, you can start multiple rails process with Passenger or Unicorn. CustomMemoryStore denies these options.
Client Security Concern
If the concern is if cookie is stolen from browser side, you can consider EncryptedCookieStore. It encrypts the session and store in the client cookie. You cannot get password if you have only cookie or the key. You need both cookie and the key to decrypt the password.
What’s the key problem?
EncryptedCookieStore is more secure because it stores encrypted password in user’s cookie, and the secret key is only available on the server. The hacker cannot get password if he only have the cookie or the secret key -- He needs both.
Of course, you can implement similar logic with CustomMemoryStore. For example, store encrypted password in server memory and the individual key is in the cookie. If you still decide to store encrypted password on the server, I will recommend to use Redis for storage. It's simple and fast compared to MySQL and MongoDB. CustomMemoryStore is not suggested because of scaling issue.
Other suggestions
Password of other system is very sensitive data, you should be very careful to deal with security problem. If it’s a public service, you should write your Term of Service and Disclaimer agreement very carefully. Besides, you should run your services with HTTPS.
TL;DR
Use OAuth if you can (Well, I know you can't)
EncryptedCookieStore should be simple and secure.
If you decide to store password on the server, please encrypt it and store the secret key on client side (cookie).

Implementing sessions in rails

I'm doing a first pass at rolling my own authentication and sessions in rails and am not sure that I understand the session support that is present. (By first pass, I mean I'm initially authenticating via http, not https. Production code will use https.)
My understanding of secure sessions is that you pass a token to the browser via a cookie over SSL, and then compare that token with the token stored on the server to see if it's really the user you think it is. I was hoping you guys could check my understanding of secure sessions, which is as follows:
User gets login page and submits login name and password (POST via SSL).
Server checks protocol and then checks sha1 of password (+ salt, usually) against existing hash in db. If they match, generate a session id, put it both in a(n SSL) cookie with the user id and in a server-side session store. Redirect user to the secured area of the site.
That session id remains the same throughout the user's logged in session --or-- the server issues a new session id after each secure operation, sending it via an SSL cookie and storing the new value in the db.
Any actions that involve private or secure data checks the session store for the existence of a session id for this user and, if present, compares the cookie's session_id against the session store before performing the action. If we're rotating session ids, issue a new session id (SSL cookie and server-side store) after the action.
User logs out, which tells the server to remove the session id from the session store and clear the cookie. Or the cookie expires on the browser and/or on the server and re-authentication is required.
Are there any glaring errors in the above? Also, it seems like Rails' session[] support wouldn't prevent MITM attacks if the token in the cookie was merely a session id. Is that correct?
I would suggest having a look at restful_authentication. This is the defacto standard auth library for Rails.
You don't actually need to generate the session_id yourself ... Rails handles all of this for you - checking the session id against the value provided by the browser. You can actually just store the user id in Rails session collection and then check that this exists.
You would technically be vulnerable to MITM attack if you do not use an SSL connection.
You seem to be confusing 'the session' and 'being logged in'. The session object in Rails is just a hash, stored in a cookie, and it is always present—regardless of whether or not the user has logged in.
As you outline, the most common procedure is to store the user's ID in the session.
The restful_authentication plugin does a lot of things. Perhaps you find my Blank Rails App more helpful, as it does something similar with a lot less code. Take a look at the sessions controller and lib/authentication, where the authentication related controller code is defined.
Try this web site, http://www.quarkruby.com/2007/10/21/sessions-and-cookies-in-ruby-on-rails. It appears to have a pretty comprehensive coverage of the subject.
One suggestion that I would have would be to not only use SSL but also encrypt and encode (Base 64) the session and other cookies that you send. Include a nonce (random value) with the session id so that the encrypted/encoded version changes every time you send it. If you are genuinely concerned about the session being hijacked you could also regenerate the session id periodically to limit the exposure of a hijacked cookie, although encrypting it should protected you if the cookies aren't persistent.
You should be able to use the encryption/encoding idea even if you use query parameters for the session id instead of cookies.

In Rails, with cookie-based session store, are session and cookies the same thing

I've always been using the cookie-based session store, and never even knew about Cookies until now. So is there any situation where I'd need the cookies hash?
The cookies hash definitely has value in Rails apps. You should use cookies to store values on the client side that you want to remember between sessions.
A 'remember me' token is a great example. If you want to allow a user to be auto logged in when they visit your site, just store a persistent cookie with some user tamper-proof value (like a unique hash or guid (good) that maps to that user's row in your db but isn't hackable like just using a plain old integer user id (bad)). Then, when a user visits your site, you can check the cookies hash for a remember me token and, if found, do a lookup in your db and log the user in if a match is found. This is a very common practice.
If you need/want to store plaintext values in the client side cookie, but don't want the user to be able to futz with the values, just store a hash of that value in a companion cookie and salt the hash with some value unknown to the user. Then you just need to compute the salted hash of the plaintext value received from the client cookie and compare it against the hashed value also passed from the client cookie. If they match, you can trust it.
any situation that might use a cookie seems to be equally well served by the cookie session store. the rails cookie session store is secure in the sense that the end-user can read the session data but cannot modify it.
Yes I got really confused about the relation of sessions with cookies while thinking how to implement remember me for OpenID login... which actually doesn't differ from doing it for password-based login. But that wasn't my code, it came from the restful-authentication plugin, and there's nothing like thinking through the whole process on your own.
You shouldn't store anything you don't want the user to see or change in cookie. If you store a member ID then the user could easily change the value and pretend to be someone else. Cookies are also sent with every single request to your web server including image, JS and CSS requests. If you are storing lots of information in cookies, this could have an impact on speed.
Cookie-based sessions (in a general context, I can't say I know what Rails does) means your session variables are associated with a session ID which is randomly generated. This ID, and only the ID, is returned to the the user as a cookie. This allows you to associate the users request (because you have session ID cookies) with the user's sessions. This is safer because it would be very difficult for someone to guess the ID of another user's session.

Resources