Security Model in Rails Question - ruby-on-rails

I am reading a great Rails tutorial and came across a passage that I had a question about:
Box 9.2.Sessions and cookies Because
HTTP is a stateless protocol, web
applications requiring user signin
must implement a way to track each
user’s progress from page to page. One
technique for maintaining the user
signin status is to use a traditional
Rails session (via the special session
function) to store a remember token
equal to the user’s id:
session[:remember_token] = user.id
This session object makes the user id
available from page to page by storing
it in a cookie that expires upon
browser close. On each page, the
application can simply call
User.find_by_id(session[:remember_token])
to retrieve the user. Because of the
way Rails handles sessions, this
process is secure; if a malicious user
tries to spoof the user id, Rails will
detect a mismatch based on a special
session id generated for each session.
For our application’s design choice,
which involves persistent
sessions—that is, signin status that
lasts even after browser close—storing
the user id is a security hole. As
soon as we break the tie between the
special session id and the stored user
id, a malicious user could sign in as
that user with a remember_token equal
to the user’s id. To fix this flaw, we
generate a unique, secure remember
token for each user based on the
user’s salt and id. Moreover, a
permanent remember token would also
represent a security hole—by
inspecting the browser cookies, a
malicious user could find the token
and then use it to sign in from any
other computer, any time. We solve
this by adding a timestamp to the
token, and reset the token every time
the user signs into the application.
This results in a persistent session
essentially impervious to attack.
I don't understand what this is saying. I take from it that a unique session ID is created and stored on the client in a cookie. Then when that cookie is sent to the server on a request, the server knows that is the user in question so that the login can be persisted. However, if a malicious user stole the cookie, I don't understand why they can't log in from another computer. The author says this is solved by adding a timestamp, but I don't see how that helps. Further, the author says that the token is reset every time the user signs in, but the whole point is a persistent sign in, so I don't understand. Please help!

You are correct—a "Remember Me" cookie can be used to steal a login. The issue that they're trying to resolve are if someone steals your cookie, containing your unique identifier, and hangs on to it—they'd then be able to log into your account at any point in the future.
The usual solution is to invalidate all previous cookies every time that you log into your account using either the username/password or the "Remember Me" cookie, so that a given cookie will allow you to login a single time. The timestamp is how they're ensuring the uniqueness of each cookie.
If you're worried about cookies being stolen, a typical solution is to also store the IP address that the request came from, and if the IP address that the cookie is coming from doesn't match the IP address that the cookie was created from, deny the login and force the user to sign in. This can be inconvenient to users who are behind dynamic proxies, or who carry their laptop to and from work/home/coffee-shop, since their IP address will change all the time.
"Remember Me" is a security hole by design. The goal is to limit how much of a hole it is, and if you're designing a system that requires absolute security, it's not a good choice. If convenience is more relevant than security, using timestamps and cookie invalidation limits the potential security issues.
If you're interested in more information on this topic, the Security Guide section of Rails Guides has an entire section on sessions.

Related

Storing data in session cookies

I am looking through the Ruby on Rails tutorial book by Michael Hartl. In chapter 9 he talks about session cookies being removed after the user closes down their browser. To make sure a user is logged in during subsequent visits a separate and persistent cookie can be created.
He described session cookies (as implemented by rails) as being secure. So the question is why have a session cookie that expires? I understand that it will no longer have to be called a session cookie and you would have to make sure the cookie was under the 4kb limit. A session cookie would then act as a secure "remember me" for the remainder of its life.
Why don't sites make their session cookies permanent?
In the Ruby on Rails tutorial, he also told us the four main ways to steal cookies. Keep that in mind.
We have to distinguish two types of cookie. One is persistent, the other is per session.
Microsoft defines them as:
Persistent cookies are stored for a length of time that is set by the Web server when it passes the cookie to Internet Explorer. These cookies are used to store state information between visits to a site.
Per-session cookies are used to store state information only within a session. These cookies are cached only while a user is visiting the Web server issuing the per-session cookie and are deleted from the cache when the user closes the session.
We use the persistent cookies to tell who the user is. Nevertheless, the expiration date is just up to you. You can set it whatever you like. But when it expires, the user have to log in again.
For the developers, maybe someone think it's more secure to let the user to remember and type it again and again.
Honestly, it would be much safer to ask the password when the user is doing something critical even after they logged in.
I believe the session is in fact persistent and many sites I have worked with have made them permanent and used them to log a user in and keep track of them.

OmniAuth without Devise: How can I securely implement Remember Me?

If I just store the provider and uid in a cookie is it secure enough? Or should I encrypt one or both of them? Should I augment provider and uid with a secure token?
Are there any other considerations that I should take into account?
You can use a signed cookie. These are cryptographically signed making it impossible to alter their data without invalidating them. This kind of cookie is typically used to store Rails session id/content.
Keep in mind that these cookies can still be decoded (it's just base64), but it shouldn't be a problem as provider and uid don't need to be kept secret.
If you don't want the cookie's content to be readable, you can use an encrypted cookie.
You can read more about the different types of cookies proposed by Rails here in the ActionDispatch::Cookies documentation
You say you can sign a user into your website when she visits if you've stored the provider and uid values you get from omniauth in a cookie. The problem with this is that it's not reliable as a means of authentication. Do you want to actually authenticate the user of your website? Then you need much more than just data that will allow you retrieve a unique user id from your database, as you recognize. You need some sort of guarantee that the user id you're associating with the session represents the user you think it does.
"Remember me" essentially relies on the assumption that the user-agent (e.g., the browser) is used only by the user who you originally authenticated. Can you or your user be sure of that? (This is why websites require you to opt-in to "remember me" - when you check that box you're promising that nobody who doesn't have authorized access to your user account has access to your user-agent.) It's not hard to see that this is pretty much essentially insecure. You can sign or encrypt your cookies, but unless you know that the user-agent is only accessible by the user you authenticated, you don't know that the user who visits your website the next time is authorized to access the original user's account.
If you're using omniauth, then you're essentially relying on some 3rd party to authenticate your users for you, either as a Relying Party as defined in by the OpenID Connect specification, or using some kind of non-standard authentication scheme on top of OAuth 1 or 2. What you're essentially asking is "Can I securely authenticate a user via a 3rd party just once and then safely assume that any time the same user-agent visits my site, it's the same user?"
The answer is NO.
But admittedly, there's a trade-off here between security and usability, and some people might think the risks (if the user's account isn't that sensitive) are outweighed by the usability benefits. However, if you're relying on a 3rd-party provider to authenticate your users, then the usability difference is almost literally zero. If the user has already authorized your application on google, facebook, or whatever other provider, and if they have a current session with that provider, then when they click the "log in with [provider]" link on your site, they can be logged in with no additional interaction from the user. No passwords or usernames to remember and enter, nothing. So the worst that can happen is they have to log in to google, facebook, or whatever, if they aren't already.
What's more, if you do this, you get more assurance that the user is who they say they are. Each time your user "signs in" with an OpenID provider, you get an id token that says who they are, that they were authenticated by by the provider, when that authentication event took place, etc. So you're not just assuming they're the user with a particular provider uid, you're trusting the provider's assertion that they are. Of course, even this is not perfectly secure, because it's possible the user's provider account is compromised, or in theory the provider could be untrustworthy. But it's still an improvement over "remember me."
TL;DR "Remember me" is inherently not secure, and offers no meaningful usability improvement over requiring sign in via an OpenID provider on each visit. Don't do it.

How do you prevent session hopping using ASP.Net Web API?

Creating an angularjs single page application trying to use a RESTful API and I came across a problem that I can't seem to find an answer for. I was trying to prevent session hoping, one user logged in watching the requests figures out his account ID is 13. So he performs the same request but this time alters the request to get info for account ID 14.
So after the user logged in I was setting a session variable to his account ID and was going to strip the account ID out of the ajax requests. I then tried to access the Session from a class inheriting from ApiController. After looking around I realize that storing session information is not very RESTful.
So how would I go about ensuring that one account cannot access another account's information just because they watched the ajax requests and figured out how to manipulate the ajax request?
Is restful not meant to be used with users that need to authenticated and authorized? I was thinking of maybe hashing the IDs or something but I am not sure that is the right approach.
UPDATE:
I have token based authentication working now. But I am still in the dark as to how to prevent someone from fiddling with HTTP request and getting information that doesn't belong to him. For Example, I want to retrieve all the users for account with ID 14.
HTTP Get /users/14
I have a token so I know that the person trying to use the web API at some point authenticated themselves. I can lock it down to roles. But there is nothing stopping this authenticated person form fiddling/hacking with the request and doing the following
HTTP Get /users/58
Now the person has got all of account with ID 58's information. This is information does not belong to account 14 but now he can browse through other people's information.
I need someone of knowing that the request that comes from the browser and says it is for account with ID 14 that it really is account 14. I can put that information in the token but how do I check it in a handler or check it in the ApiController?
The problem you have described is not something unique to REST-based services. In fact, this is one of the top 10 OWASP vulnerabilities (Insecure Direct Object References). You will need to first identify the user and then authenticate the user. For identification, an ID is used, such as user ID. The identifier can be anything you want. After identification, you need to authenticate the user. This is done by authenticating the credential presented to the app, such as password. The identifier and the credential can be any thing, say the good old user name/password, or may be a token or may be an API key. It actually does not matter to the problem on hand. Once authenticated, you authorize the requests based on what the user can do in your app. The authz part is a must regardless of whether you use a token, certificate or what not. Hashing user ID or using some method to hide things is security by obscurity and is not a good practice.
Say, you are using a message handler to authenticate the credential submitted by a user. Once authentication is done you can store the account number associated with the authenticated user in the properties collection of HttpRequestMessage. This is done in the server side and no user can make the system store some random account number. Only the account number associated with the authenticated user is stored in the properties collection. With that, you can now authorize requests. Say, you can write an authorization filter that pulls this account number and compare it against the account number in the URI to determine if the request is allowed or not. By applying this filter against a GET action method, you can ensure only right folks can see right data.
For example, a user with user ID 'abc' and password 'pwd1' makes a GET request to /users/14. First step is, you will authenticate the user ID. If there is a user with ID 'abc' and password 'pwd1' in your store, you will consider the user authentic. As part of this authentication, you can pull the account number associated with the user 'abc'. Say it is 15. You will store 15 in request.properties. From your authorization filter, you can get the account number from URI, which is 14 and compare it against the one in the request, which is 15. Obviously the numbers do not match and you reject the request in the OnActionExecuting method of your filter with a 401.
What I described above is a simple approach. Better one (in terms of extensibility) will be to use claims but it will not be possible to explain all that here. You can get good amount of information related to claims-based identity from Dominick's blog.
Every request should be authenticated. If the credentials provided do not allow the user with account 13 to access data from account 14 then the request will be denied. The trick is to find a way to do authZ quickly.
You seem to be missing the information on how you want to implement authentication. As you correctly noted, using session to keep authentication information is not very restful. Here are the alternatives
Implement your own OAuth provider or use third party (for example
Azure ACS)
Implement STS provider (this is only for soap though)
Implement a custom token system, if you don't want to deal with
the above two. Basic system would take user id, salt it and encrypt with private key - but don't quote me on how secure that would be...
In all the cases, the authentication information is stored in the cookie, similar to session cookie. But it is still stateless.
The standard way on the web seems to be the oauth way, in fact the standard VS MVC template even comes with OAuth consumer implemented out of the box. So the question is, how do you link the oauth token to your internal user ID. That's really pretty simple, as you implement the "registration" step after the new user is authenticated - you keep user ID and oauth token in the database table, to link the two.
That link is quick to retrieve, and can be cached for speed. I have heard some people implement that type of credentials caching for some very big systems (google docs).

Security of Cookie-based sessions

I need some clarity around how cookie-based sessions work. I'm building an app where I authenticate a user and upon successful authentication, I stick a GUID identifying his user into the session, which in turn gets persisted as a cookie. Now when a user logs in, whats to prevent someone from sniffing traffic, stealing the contents of the user's cookie and creating a cookie on their own end and login to my site as that person? Another scenario could be if I had physical access to a machine where the person was logged in, I could also steal the contents of the cookie and impersonate as the user.
Whats to prevent someone from sniffing traffic, stealing the contents of the user's cookie and creating a cookie on their own end and login to my site as that person?
SSL - the only way to stop that is to run your web site on HTTPS.
I had physical access to a machine where the person was logged in
Once you have physical access to a machine all your security methods are moot. You can do nothing about this.
I think you have two questions here. In regard to the second you should not be storing a session key in a cookie and have it stick around longer than the session, set the timeout on the cookie to expire quickly and invalidate the session on the server as soon as reasonable and the cookie becomes useless. If you are flowing important information over the wire use https.

Verifying a user when backing up data to a server

Note: Although I raise this issue in the context of an iOS app, I don't think it's confined to an app running on that specific OS.
I'm developing an iOS application that will back up user data to a server and I'm trying to figure out the best way to verify server-side that the user being updated is actually the real user. Each user will have an id (uid). If that's all I depended on server-side, then I imagine the process would go like this:
User runs app for the first time
Creates account in the app, which communicates with the server to both create the account on the server and to get a unique "user id" (uid)
App stores this uid so that it can identify the user in subsequent communications with the server
However, if someone were to hack the app on their iphone, they could change the user id value and then that would instantly give them access to/allow them to modify a different user's data.
The current solution I'm considering is that the user receives 2 unique ids, the uid (just an auto-incremented number) and a longer, more complex key string. All communication with the server will therefore have to send along both the uid and the key. The server will verify that they match in order to make sure that the user truly is who the app says it is.
So, my question is two-fold:
Is this the correct way to achieve this? Or is there some other standard method that I should pursue?
If this is the correct approach, what's the recommended way to generate the unique key?
First of all, you can use the more complex value as the user ID to begin with, if you like (e.g. a UUID). Monotonically increasing IDs get hard to manage as your service scales.
You have the same problem a secure web site does when it leaves secure cookies on the browser to remember a session. Those cookies do include the user ID, but must prevent tampering. This is generally done by signing the cookie on the server before sending it back.
So what you'd do is:
Generate the user ID on the server, and use it to create some sort of "auth token" for the client to have to sign in.
Sign the auth token on the server with a secret key that only your server knows.
Send the auth token to the client, where it is stored for all subsequent logins. Transfer the auth token over HTTPS to prevent someone else from snooping it on the network.
When the app goes to login, send up the auth token to the server. If it's been hacked, the signature validation will fail, and you'll know to reject the client.
Consider including a timestamp in the signed token as well, so it expires after some time, forcing the server to regenerate an auth token periodically, which protects you in case your key is compromised. It's hard to do this all fully unless the user himself has a shared secret/password he can use to authenticate periodically as well. Depends on how far you need to go.
Other considerations: If all you know about a user is their generated UID, you don't have any way for that user to come back later from a different iOS device and restore their account there, right? Generally, if the user will be creating anything "valuable" in their account that they'll want access to later, you'll probably want to create a more traditional user account backed by an email address and password or the like, so they can access the account again after reinstalling your app. (This may or may not be relevant to your case.)
I would recommend going the "standard web browser way" and just letting the user set an email (login) and password.
When the iOS device connects to the server (using HTTPS), it uses regular "basic authentication" to log in, and receives a cookie which is valid for a set period of time. As long as the device keeps requesting data from the server within the cookie's lifetime, the cookie is renewed, and when the cookie is expired the server will automatically challenge the client to log in using its stored information again.
This has some advantages;
The user can log back into his account with a new device with a regular password reset. Easy, straight forward solved problem.
There is no special solution on the server side, any server side script can require authentication just like it would for a browser - built in functionality.
You would not have to invent your own security scheme. This scheme is used by millions of browsers every day to authenticate to web sites.
Not tied to a special phone, if the user has several iOS devices, he can use the same account from all of them by just logging in. No special set up procedures.
In other words; no special solutions for you to develop, generally solved problems how to handle login information, proven security and ease of use.
According to me, you can't really beat that :)

Resources