Rails4 security: Session fixation possible at all using encrypted cookies? - ruby-on-rails

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.

Related

rails session steal through cookies

My question is different it's rather conceptual - As when a user login, session is created on server and it stores a unique id to client(browser) what if I copy that cryptographically signed cookie and any associate data from browser like token whatever app uses to recognize the machine, paste it or create it on another machine?
How would server recognize that? could someone Explain me as much you can? that would be a help
I tried finding the solution.
or how can I secure that? :)
As far as I know, only the user-identifying token in the Rails session cookie identifies the user. By sending that token (which happens automatically on each request), the server knows who you are. Anyone having that token will be treated by the server as if it were you. This is called Session hijacking.
There are a few things you can do to protect your user's cookies. First of all, secure your cookies by setting two flags:
secure tells the browser to send that cookie only over HTTPS, so it is protected against someone reading your traffic (and your cookie).
HttpOnly tells the browser to hide that cookie from Javascript, which improves protection against XSS (Cross Site Scripting). An attacker may find a way to inject some malicious Javascript, but the browser won't give it your session cookie.
On setting these flags, see the Rails API. For custom cookies it's basically:
cookies[:my_cookie] = { value: '...', secure: true, httponly: true}
For configuring the Rails session cookie, see this answer.
Recently, I have written a middleware that sets both flags automatically to any cookie in the application. It is called safe_cookies and we're using it in order to protect our applications.

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.

session fixation

I am new to web development, and trying to get a hold on security issues. I went through this article on http://guides.rubyonrails.org/security.html these are some of the steps the author has mentioned how an attacker fixes session.
The attacker creates a valid session id: He loads the login page of the web application where he wants to fix the session, and takes the session id in the cookie from the response (see number 1 and 2 in the image).
He possibly maintains the session. Expiring sessions, for example every 20 minutes, greatly reduces the time-frame for attack. Therefore he accesses the web application from time to time in order to keep the session alive.
Now the attacker will force the user’s browser into using this session id (see number 3 in the image). As you may not change a cookie of another domain (because of the same origin policy), the attacker has to run a JavaScript from the domain of the target web application. Injecting the JavaScript code into the application by XSS accomplishes this attack. Here is an example: <script> document.cookie="_session_id=16d5b78abb28e3d6206b60f22a03c8d9"; </script>. Read more about XSS and injection later on.
The attacker lures the victim to the infected page with the JavaScript code. By viewing the page, the victim’s browser will change the session id to the trap session id.
As the new trap session is unused, the web application will require the user to authenticate.
From now on, the victim and the attacker will co-use the web application with the same session: The session became valid and the victim didn’t notice the attack.
I dont understand couple of points.
Why is user made to login in step5, since session is sent through?
I saw possible solutions on wiki, like user properties check and others. Why cant we just reset the session for the user whoever is login in when they enter username and password in step5?
1) The attacker receives a session in steps 1&2 which is not logged in yet. This is the trap session. At step 5 the victim logs in thinking that the session id is new (and 'secret'). At the moment when the victim is logged in the attacker is able to re-use the 'secret' session id and effectively is logged in as well.
So to answer your question: the victim is made to login because the trap session is not logged in yet in order to trick the victim to login using this session id.
2) After explaining the steps of session fixation, the first counter measure (section 2.8) is to create a new session and abandon the old on after logging in. Exactly what your idea was!

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