Implementing sessions in rails - ruby-on-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.

Related

Rails4 security: Session fixation possible at all using encrypted cookies?

After studying the rails guide and some other ressources I'm wondering how a session fixation attack on a user's session can actually happen. At least I'm sceptical it works as simple as depicted here in the guide, where an attacker...
1) ...creates a valid session by logging in
2) ...keeps the session alive
3) ...then forces the user to use his session's id e.g. by using some XSS vulnerability
All fine, but... how would the attacker be able to gather the value of his own session id? By default cookies are encrypted in Rails4+. So what to do as an attacker assuming I do not have access to secret_key_base or whatever is used to generate the encryption and signature keys? From what I understand I cannot tamper with the cookie without invalidating it (signature wrong) so somehow passing a self-created cookie to a possible victim is neither an option.
Is the secu guide kind of not up to date or am I missing a point here? If the latter then...
a) how [as an attacker] can I read encrypted cookie information
b) how does a vulnerability have to look like that allows me [the attacker] to inject that session id into the likewise encrypted cookie on another client? Would that be an XSS attack? The guide states that if an attacker uses code like
<script>document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9";</script>
he might be able to fix that session. But again, why would rails reveal it's plain session to the client making it accessible via client-side processed javascript? It does not, which is why all my cookie-values are simple gibberish not being accessible by Javascript (can test that via console), right?
Thanks for any advice!
It's not so much about the cookie encryption (or signing). Without signing and/or encryption, the attacker wouldn't need to resort to session fixation with a site that stores session data in a cookie; they could just craft their own cookie.
Assume the attacker has a way to inject cookie-setting JavaScript onto a page that a victim will visit, i.e., an XSS vulnerability. Here's how the attack plays out:
The attacker visits the site and gets a session cookie.
The attacker crafts malicious JavaScript, exploits the XSS vulnerability, and waits for a user to visit (or lures a user to) the page containing the malicious JavaScript payload.
<script>document.cookie="_appname_session=(...attacker's session cookie...)";</script>
A victim comes along and visits the page with the malicious JavaScript, setting or overwriting their session cookie with the one from the attacker.
If they were logged in, the site will no longer recognize them as an authenticated user, and will probably redirect them to log in.
If they weren't logged in, they will be redirected to the login page.
They authenticate with their username and password.
At this point both the victim and the attacker have a cookie containing a session_id belonging to a user who has authenticated.
CookieStore, the Rails default session store
Normally you will set some value in the session to indicate that the user is authenticated. Even if it is as simple as session[:logged_in] = true, it will change the value of the cookie, and the attacker won't be able to access the site, even though the session_id is the same, because the attacker's cookie does not contain the additional data indicating an authenticated session.
Other session storage strategies
Here's where this attack is more feasible. Other session stores still use a cookie to hold the session_id, but they store session data elsewhere — in the cache, in a database, in memcached, etc. In this case, when the attacker visits the site after the victim has authenticated, the attacker's cookie — with the same session_id — will cause the session to be loaded.
HttpOnly
However, there is another major impediment to this attack — HttpOnly cookies.
Set-Cookie: _appname_session=v%2Ba...; path=/; HttpOnly
When a cookie is designated HttpOnly, as Rack/Rails session cookies are, the browser does not expose it through document.cookies. It can neither be read nor overwritten by JavaScript. I tried setting a known session cookie via document.cookie, but it was not possible unless I cleared the existing cookie(s) first. This means that an attacker would need a user who doesn't have a session cookie yet to visit the page with the XSS exploit (which would need to be on a page without a session cookie) before logging in.
As I understand it, session fixation won't work if the session details are stored in the cookie itself. The old fashioned way of doing sessions, the session data would be saved in some sort of database. The session id that got saved in the cookie would point to the record or file where the session data was saved. This means as the session changed the cookie wouldn't change at all. If a hacker had the same cookie as the user, when the user logged in they could potentially be able to hijack the login since their cookie would point to the session that now had a user_id (and potentially other data) attached to it. Under the new system, any change to the session also changes the cookie so the hackers old cookie would just have an empty session.
That being said, it's still a best practice to add reset_session to login, logout and registration endpoints (if you use something like devise this may be done for you) since if you or some future developer changes the session store to be database backed you could end up shooting yourself in the foot. The security guide also doesn't know what type of session store you are using so they have to include that advice.

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).

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).

Should the SessionID in the QueryString or the Cookie of a GET request take precedence?

If I receive a request to the url host.com/site-directory/page-slug.html?session={someValidNonExpiredSessionGuid} and I detect a session cookie with the value: {someOtherValidNonExpiredSessionGuid}, then
Which session is the correct session to associate with the request?
Here's some background:
The pattern I am using for maintaining state across HTTP requests is to store a unique ID in the querystring, form collection and/or cookie. Then, on each request, I locate the unique Id and pull date from a database table. If the id is not valid, or the session is expired, a new session will be created (with a new id, a new cookie, etc).
The default behavior is that all forms will have a field:
<input type="hidden" name="session" value="{someGuid}" />
All links to other pages on the site will have an appended querystring parameter
a sample link
And, if the user's browsing device supports cookies, a cookie will be set having the session's value.
If I have validated that the user's browsing device supports cookies, then my form and querystring will no longer require the the session id field/parameter.
However, I am having a conceptual issue in deciding whether the session parameter of the querystring should take precedence over the cookie value or vice-versa. It seems like I could potentially get bad data in either circumstance. I could get data with a bad querystring parameter if the user bookmarked a page with the session parameter included in the URL. I could also get bad data from the cookie, if a user closes the browser without terminating the session, and the session expire-window has not yet closed. It also seems like both options could be vulnerable to a malicious user intercepting the request and sending a request with the same session information.
So, once again, my question is
If I receive a request to the url host.com/site-directory/page-slug.html?session={someValidNonExpiredSessionGuid} and I detect a session cookie with the value: {someOtherValidNonExpiredSessionGuid}, then Which session is the correct session to associate with the request?
I am leaning towards the cookie session, because it seems like the most common scenario will be a bookmark with the session included. I've already decided that the form post data should take the greatest precedence, because a page will always render the form with the correct ID, and the only possible situation with a wrong, non-expired ID is a very quickly implemented XSS attack, which is circumvented by including a request-scoped anti-forgery token field.
In addition to the primary question I appreciate any insight to any security-related or logical oversights I have expressed in this description. I apologize for the long post, but felt it was necessary to explain the situation. Thank you very much for your input.
Also, it is not necessarily relevant to the question, but I am using ASP.NET MVC in most situations, and setting my cookies manually with Response.Cookies.
From a security standpoint sessions should not be stored in query strings.
For example:
If sessions are stored in queries and you link to a remote host on the same page, the users valid session could be sent to the remote host via the referer header.
Sessions should always be stored in cookies.
You should try to store it in the cookie (looking the the browser caps to see if the browser supports cookies) and then have a fall back for query string.
I too would lean towards using the cookie session ID in case of ambiguity. This primarily because I'd trust the cookie implementation to be more well baked, more well tested, and more idiot-proof than my own home brewed session tracking implementation. For e.g. there are cases where ASP.NET automatically knows to clear the session cookies, renew them etc.
Also, I would design it so the cookies aren't persistent to minimize the edge cases. If the user closes the browser, then the session is closed.
The other simplification I would consider is to have either cookies or URL based tracking and not both. If the browser supports cookies, there is really no reason to also track the session through a URL.
Thoughts?
Curious ... What were your reason to rule out using the stock ASP.NET cookieless session implementation? - http://msdn.microsoft.com/en-us/library/aa479314.aspx#cookieless_topic2
If I receive a request to the url
host.com/site-directory/page-slug.html?session={someValidNonExpiredSessionGuid}
and I detect a session cookie with the
value:
{someOtherValidNonExpiredSessionGuid},
then Which session is the correct
session to associate with the request?
To answer your specific question, I'd recommend putting session management in the cookies as opposed to the querystring. As has been mentioned, cookies can be set to expire whereas the querystring cannot. This allows your thin-clients to assist in their own session maintenance by removing their own expired cookies. Moreover, since cookies are dropped to specific browsers, you reduce the chances of another browser spoofing the original browser session.
The only way I would use the querystring to pass session information would be as a backup method to re-establish a browser session onto a new browser instance. Here's a scenario: you have an active session using browser A on machine A which suffers some catastrophic error. You want a way to re-establish that same session on another browser instance on either the same machine or on another machine. If your code-behind can recognize that the session cookie doesn't exist, but that a valid session id exists in the querystring, you could initiate a challenge-response to verify the integrity of that session id and then drop a new session cookie on the new machine. Kinda extreme in my humble opinion, but the functionality might be useful in certain situations.
ADDED: I understand that you may want to accommodate users who have turned cookies off on their browsers, and while you can use the querystring to hold the session id I'd recommend against it. But if you must, encrypt that sucker using browser-machine specific information.

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