I have two models doing login (Devise) in my Rails app - Admin and User, both currently use the default cookie store for session data.
I want to be able to identify an Admin session in AJAX requests coming in from the admin, for authorization of these API calls. I plan to do this by setting an encrypted cookie upon Admin login. When the AJAX API call comes in, I open the cookie, grab some identification from it and look for a matching existing Admin session in the store.
As I understand it, to do this, I must have session information stored in the back-end, either by DB or memcache stores.
I expect to have millions of sessions of Users and just a few sessions of Admin at any given time. For this reason, I don't want to just move all session information to a DB or memory, since this is a heap of unneeded data to store. I only want to store/look at Admin session data.
A solution will be creating some custom model which enumerates Admin user sessions, and is maintained by the app. This is simple enough but requires for instance, a way to clean up sessions when they die without signing out. Essentially this is an attempt to duplicate Rails's session store mechanism, which comes with all the problems of storing and maintaining sessions. Instinct tells me to avoid this solution. Am I correct to avoid it?
If so, then my question is, is there a way to configure multiple session stores in a Rails app, a different store for every logged in Model? In this case, have Admin sessions stored in memory, and User sessions stored in cookie. If not, I'll greatly appreciate any comments and suggestions.
Thanks!
You may be thinking about it wrong.
Session are a low level mechanism that you build your authentication on top of. Its just a cookie containing an identifier (a random hash) which is linked to a session storage (by default cookies). This is a simple mechanism to add persistence to a stateless protocol.
Confusingly we also use the concept "sessions" when talking about authentication - for example logging a user in is often referred to as "creating a session". This is complete poppycock as we are just storing a claim (often a user id) in the session that was created when the user first visits the application.
If so, then my question is, is there a way to configure multiple
session stores in a Rails app, a different store for every logged in
Model?
No. Thats a chicken-vs-egg conundrum. In order to know which session storage to use you would need to access the session storage to know which session storage to use... you get the picture.
While you could create your own session storage mechanism that works differently does this is most likely a complete waste of time. Premature optimization is the root of all evil.
As I understand it, to do this, I must have session information stored
in the back-end, either by DB or memcache stores.
Not quite true. You can perfectly well build an authentication solution with just the cookie storage. In that case Rails just keeps a record on the server of which session identifiers are valid.
The main reason you would need to store additional session information in the database or memcached is if you need to store more data in the session than the 4093 bytes allowed by a cookie. Cookie storage is after all much faster and does the job fine 99% of the time. YAGNI.
You should also recognize that not everything needs to be saved in the session storage. For example the Devise trackable module saves log in / out timestamps on the user table as part of the process of authenticating a user. This is "session information" yet has nothing to do with session storage.
I want to be able to identify an Admin session in AJAX requests coming
in from the admin, for authorization of these API calls.
There are many ways to use different authentication logic for different parts of the application such as Warden strategies. For an API you may want to consider using stateless (and sessionless) authentication such as JWT.
Related
I have an application that has an actual map of objects that any visitor can view as long as they have the correct access code. I don't require the user to login because I don't want to make them create an account as it is unnecessary. I want to allow the users to mark the objects on the map with a check and save the edits within the session. So if the user refreshed the page or they close the application and reopen it an hour or so later, I would like to save their marks based off their session id. But I am confused on how to set this up without requiring them to login because I am unsure how the sessions would work.
Any help would be greatly appreciated!
Sessions in Rails work the exact same way regardless if you have a proper authentication system or not.
When a first time visitor visits your application the sessions middleware creates a session identifier. This is a cryptographic hash that is kept by the server and also passed to the user in a cookie.
This lets you identify users across requests.
This session identifier is also linked to a session storage. By default this is ActionDispatch::Session::CookieStore which lets you store session data in a encrypted cookie held by the client. This is where you would normally store a user id. Since its a cookie the amount of storage space is very limited. If you want to store more data in the session you can use a different store such as Memcached, Redis or ActiveRecord.
But what you may want to consider is creating (guest) user records implicitly without the normal sign up procedure. I would just use Warden and have a bare bones user model and a cron tab that cleans out unneeded data periodically.
This gives you a database oriented application model where you can use associations and build it like a standard rails application instead of the untestable mess that results when someone goes bonkers with sessions.
I would implement Cookies (with their permission of course). You can store basic data in it, or even create a sort of ID for them so when they return you can restore their settings
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).
I am learning Rails by reading the online guide(for Rails v2.3). The guide is great, however, there is a confusion for me, that's:
there is a chapter explains the Session of Rails and another chapter explains Cookies of Rails. The explanation is easy to understand separately, but when compare the two, reader like me does not see the significant difference between Session and Cookies . Especially under which situation session should be used and under which situation Cookies should be used ?
Besides, in the session chapter, there is a concept of CookieStore , what is the difference between the CookieStore and Cookies then?
Could someone explain to me these?
Sessions & Cookies both hold the ability to store some information (e.g : the current_user id) in between two or more requests which (in http) are otherwise stateless.
But Session is more of an abstract concept related to the notion of being in a certain state for a specific amount of time : the info it contains can be stored in the database, in a server side file, in a redis hash OR in a cookie.
Cookies are always the little text file navigators have to store some persistent data in between requests... But having some data on the client side can be insecure so that's why it is often encrypted. But it's true the notion can overlap with session.
TL;DR : session the abstract concept of holding temporary data. Cookies one (common) way of doing it.
A cookie is a small text file stored in the browser.
A session is the concept of a state of being "in-use", and that state can have data associated with it. Rails keeps track of sessions with cookies, and lets you choose different storage for associated data and access it with the same session interface.
CookieStore means all the session information is stored inside the cookie itself. You can choose to use various other stores where appropriate, and it'll still be available with your session accessor methods.
In addition to the session, you can set other cookies to store information on the user's browser. These are not tied to the session and can be set, accessed and deleted independently.
Example 1, storing a logged-in user's shopping cart in a session:
session[:embarassing_products] = ['ooh',
'naughty',
'lucky_im_using_activerecord_store',
'only_the_session_id_is_in_the_cookie',
'other_data_arent_in_the_browser']
The shopping cart is preserved for the user's session. You can set the session to end when the browser window is closed, when the user logs out, or when a certain amount of time passes.
Example 2, remembering a browser's last language preference for your domain in a cookie:
cookie[:lang] = 'en-US'
This information is stored inside the cookie itself. Unless the cookie expires or is deleted (by you or the user), it stays inside the browser.
As to me the main difference is that the session data stored on the server, whereas the cookies are stored on the client (browser).
So you can trust the data from the session. Information from the cookie can be manipulated, stolen, and thus should not be relied on for critical use (for right access for example).
Second point, is that cookies have a limited size, and are only text-based. You can store in session many complex objects (but beware of memory consumpation), and you don't have to transfer them to client then back at each request.
And typically the session only persists until the user shuts down their browser. That's useful for typical logins. Whereas if you needed information to persist between sessions you could use a cookie with a longer duration, for example a 'remember me' flag that persists even after the browser is restarted.
ok so, i have this dilemma on how i should save login credentials in mvc at the same time avoid as much hit on the database. i know i can easily use Forms Authentication to save a User instance but is it advisable?
At the moment the way I do it is I store the User Id in a cookie which i then would access everytime an Action gets called that would "require" a login access. Before the action gets accessed the User Id will be used to retrieve a "New" User instance. This will be the same on every Action, I don't store the User in the cookie as I feel like once the cookie is compromised everything about the User shall be available for the hacker (Userid, email, roles, etc)
So if i have a ton of actions that would require a login that will be difficult on my bandwidth. What do you think of the method I'm using? Should I change it to have all the User object be stored in the cookie with a short timeout? Any ideas are greatly appreciated.
thanks!!
It seems like you are trying to address a bandwidth issue. That alone would suggest that you shouldn't store more than you have to (ie: session id) in the cookie.
There are two major problems (among others) for using cookies.
1) They are sent up on every request
2) There is only limited amount of information you can store.
In general, trusting anything the user gives you (that includes encrypted cookies) is bad.
How many concurrent users do you foresee having on your website? Keep in mind that the database will be able to cache certain calls. Furthermore, if you are using a ORM like nhibernate, you will get 2nd level caching there. If all else fails, could you use the in-memory session management?
The biggest problem I have with putting userid's in the cookie is the entropy of that key. Say your userId is an email. All I have to do as an attacker is guess a userid that is valid in your system, and I will "automatically" become that user. The reason why people use sessionID's and then retrieve the user is that in theory sessionID's are harder to guess.
My suggestion would be to use database session management if you are in a load balanced situation. If not, use in-memory. It is fast. Memory is cheap. And unless you are storing 10's of mb of data in session for each user, and you have 10000's of users, you should be fine.
As Ken stated, you should probably be using the standard [authorize] tags available with MVC as opposed to creating your own method.
It sounds like you pretty much implemented form based authentication and something comparable to the [Authorize] attribute.
So if i have a ton of actions that would require a login that will be difficult on my bandwidth
Forms Authentication uses a cookie and is baked into the system. If you don't want to store your user information in SQLServer there are plenty of other options.
It sounds like you are trying to implement something that is already done. In my opinion, let's leave the security stuff to people that know about security. I would suggest working within the framework provided unless you have proof that you solution needs something else!
There is a UserData property on the FormsAuthenticationTicket object that could be used to store additional data other than the Username.
I had a project that had a similar need. I stored the values as a NameValueCollection encoded like a query string:
"email=myemail#some.com&roles=Somebody&roles=Special"
(there's also a handy HttpUtility.ParseQueryString() method that is useful for getting the values back out of the UserData property)
You can use the FormsAuthentication.Encrypt and FormsAuthentication.Decrypt to convert the ticket to and from the Cookie value.
I'd like to store some persistent data for each browser (user settings), and don't want to require a login to use the site. What's the bset way to achieve this?
I've considered two approaches:
Store the info in the session cookie. The problem with this approach is that I would need to set the expiration time on the session cookie, which has the side effect of causing a user's login session to not expire when the browser closes.
Store the info in the DB and store a retrieval key in a cookie on the client-side. I'm concerned about performance issues, as this would require additional queries and possibly some deserialization to retrieve the data. It looks like Rails switched its default from ActiveRecordStore a while back due to performance reasons: https://web.archive.org/web/20120102024844/https://www.ryandaigle.com/articles/2007/2/21/what-s-new-in-edge-rails-cookie-based-sessions
What's the recommended way to achieve this?
Why not just use a non-session cookie? You can specify how long it will persist on the client, etc. See http://api.rubyonrails.org/classes/ActionController/Cookies.html
If you're concerned about the user messing with the cookie data, you can encrypt the cookie data using a technique like this (taken from http://www.neeraj.name/blog/articles/834-how-cookie-stores-session-data-in-rails):
cookie_data = {:foo => "bar"}
digest = 'SHA1'
secret = 'my_encryption_secret'
data = ActiveSupport::Base64.encode64s(Marshal.dump(cookie_data))
digest_value = OpenSSL::HMAC.hexdigest(OpenSSL::Digest::Digest.new(digest), secret, data)
escaped_data_value = Rack::Utils.escape(data)
final_output = "#{escaped_data_value}--#{digest_value}"
cookies[:user_data] = final_output
and later to read the cookie data:
Marshal.load(ActiveSupport::Base64.decode64(cookies[:user_data]))
It was actually changed from a file-based session store to a cookie-based session store - not from ActiveRecord. I may be wrong but I believe ActiveRecord was a viable option for web farms or distributed setups when a file-based store was the default. Since the cookie-based store was introduced, the web farm scenario has become a non-issue because it's stored at the client-side. Today ActiveRecord is still a viable option where you want to store a greater quantity of data than a cookie permits, when you want to cutdown the overhead of data transmitted in each request (using ActiveRecord means you're only transmitting the session_id), or if you want a centralized-session setup.
I find that in employing an ActiveRecord session store, speed is not a factor for me. In a cookie-based session you generally limit what you store in your session variables anyway, so they tend to be tokens used to lookup data in the database. If the data doesn't need to be persisted beyond the session, then it's viable to store the object in the session rather than just a token, because you're going to hit the database for the associated records anyway. If the object you want to retrieve involves an expensive retrieval operation, and you need it during the lifetime of the session, it might make sense to store that object in the session rather than just a token, and only hit the db once when the session is first established.
Cookies are great, but remember also that a user can delete them when they want, encrypted or not. With privacy concerns on the rise, and quite a few plugins available for browsers that conditionally block cookies, and software that cleans up cookies, you might want to weigh up the pros and cons of using cookies vs. a db-backed session.
#joshsz makes a good point about using a non-session cookie for persisting data beyond the session too. Remember a session has a finite lifetime.