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.
Related
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.
Because of the awesome EU directive on allowing users opt out from having cookies stored I have the following problem.
I have a message that displays at the top of the screen that asks the user to opt in or opt out.
if the user opts in, cookies are turned on using rack.policy, and the message is hidden. A cookie variable is set to say that the message should not be shown again for future visits. Perfect
If the user opts out. Cookies are disabled, fine. the message is cleared.... and a session variable is set to say don't show the message for this session. This would be fine, but it seems the session variable is saved in the same way as a cookie and the rack policy does not allow it to be displayed. The message therefore flashes up on every single page.
So first, thank you EU. Second, how do I save a "session variable" without saving to a cookie?
Unless you try going for something more exotic like local storage offered by the browser, you can't. The rails guide explicitly states:
All session stores use a cookie to store a unique ID for each session (you must use a cookie, Rails will not allow you to pass the session ID in the URL as this is less secure).
I'm no lawyer, but for what it's worth I believe that law is intended to apply to cookies used for marketing/tracking purposes and does not apply to cookies needed for the site to function like cookies used for authentication or ephemeral storage. With this in mind, you could use the rails's session cookie for must-have data and use separate cookies for other purposes that your application can disable.
I wouldn't take it as gospel, but this Wired article seems to a good job of providing a simplified explanation.
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.
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.
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.