What I am doing is rails web service API that let user create traveling log when they access any sites, by using firefox plugin.For this requirement I needed 2 things.
skip_before_filter :verify_authenticity_token in specific controller (Because I let user create it through API not the form, so I disable this).
user have to provided username and password every request (e.g. curl -u username:pass -d "..." http://localhost:3000/logs).
What I want to ask are
can I made it easier by letting my
firefox plugin ask for user login
then use cookies, so no need to send
username password every time with
request.
Does skip_before_filter :verify_authenticity_token is bad thing or necessary thing to do for this ?
Thanks
When Rails renders a form, it includes a hidden field with a long string (authenticity token). The verify_authenticity_token filter ensures that the user submitted a form that the server actually rendered (as opposed to forging a POST request, as hackers will do). If you use cookies and sessions you should really read about how this works and try to customize it to work with your plugin.
However, why not use HTTP basic authentication instead? It's slightly faster than sending a cookie on every page view and should be much simpler to set up. As you say, you can have your plugin prompt for a username/password, and then send them with every request.
If you need to store user data in sessions, though, you'll have to use cookies.
Related
This sounds a bit evil, bear with me though. It's also not specifically a Rails question even though the two sites in question use Rails. (Apologies in advance for both these things)
Imagine two websites which both use Ruby on Rails:
mysite.com, on which i'm a developer and have full access in terms of changing code etc, and also have an admin login, so I can manage user accounts.
theirsite.com, on which i have an admin login but no dev access. I know the people who run it but i'd rather not ask them any favours for political reasons. That is an option however.
Using my admin login on each site i've made a user account for the same person. When they're logged into mysite.com, i'd like to be able to provide a button which logs them straight into theirsite.com. I have their username and password for theirsite.com stored in their user record in the mysite.com database, to facilitate this. The button is the submit button for a form which duplicates the form on the theirsite.com login page, with hidden fields for their username and password.
The stumbling block is that theirsite.com handles CSRF with an authenticity_token variable, which is failing validation when the login submits from mysite.com.
My first attempt to get past this was, in the mysite.com controller which loads the page with the form, to scrape the theirsite.com login page to get an authenticity token, and then plug that into my form. But this isn't working.
If i load the theirsite.com login page, and the mysite.com page with the remote login button in two browser tabs, and manually copy the authenticity_token from the theirsite.com form to the mysite.com form, then it works. This is because (i think) the authenticity_token is linked to my session via a cookie, and when i do it all in the same browser the session matches up, but when i get the authenticity token from theirsite.com via scraping (using Nokogiri but i could use curl instead) it's not the same session.
Question A) So, i think that i also need to set a cookie so that the session matches up between the browser and the Nokogiri request that i make. But, this might be impossible, and exactly the sort of thing that the anti-CSRF system was designed to defeat. Is that the case?
Question B) Let's say that i decide that, despite the politics, i need to ask the owner of theirsite.com to make a small change to allow me to log our users into theirsite.com when we know their theirsite.com username and password. What would be the smallest, safest change that i could ask them to make to allow this?
Please feel free to say "Get off SO you evil blackhat", i think that's a valid response. The question is a bit dodgy.
A) No, this is not possible as CSRF Protection is made to protect from actions like these only. So "Get off SO you evil blackhat"
As per the question I'm assuming that theirsite.com is using Rails(v3 or v4)
B) The smallest change that you could ask them to do is to make a special action for you, so that you could pass user credentials from your back-end and the user will be logged in from their on.
That action will work something like this :
You'll have a special code which will be passed along the credentials so that the request is verified on their servers. That code can either be a static predefined code or it can be generated on minute/hour/day basis with the same algorithm on both sites.
The function that you'd be asking to make for you will be like this:
Rails v3 and v4:
This action will be POST only.
#I'm supposing 'protect_from_forgery' is already done in theirsite.com
class ApplicationController < ActionController::Base
protect_from_forgery
end
#changes to be made are here as follows
class SomeController < ApplicationController
skip_before_filter :verify_authenticity_token, only: [:login_outside] #this turns off CSRF protection on specific actions
def login_outside
if(#check special code here)
#Their login logic here
end
end
end
Check this link for further information on skipping CSRF protection in Rails
Rails 4 RequestForgeryProtection
This shouldn't be too hard to do.
You need to send an ajax GET request to their signup page, copy the authenticity_token with javascript, and then send an ajax POST to the actual log in route that creates a session with the right credentials and authenticity_token.
One tricky part is finding out their log in route. Try /sessions/new or perhaps they have the url in the form, so look at the html there. Good luck!
The other tricky part is knowing how the parameters are usually sent. Check out the form's html. If all the input tags have user_ before their name's then you'll need to structure your parameters similarly; i.e. user_email, user_password.
It's entirely possible to fetch the crsf token and submit your own form (because a log-in page is accessible to anyone!). However, it'll be difficult to know the details of their arrangement. The guessing and checking isn't too bad of an options (again, /sessions/new is how I route my log in; you should also try your route to see if they have a similar one.)
If that doesn't work, try taking a look at their github account! It's very possible they haven't paid $7 a month and it's open to the public. You will easily be able to view their routes and parameter parsings that way.
Good luck!
This is impossible. The anti-csrf works like you send cookie to an user, and inject token in form of hidden field into a form; if the token matches with cookie form post is accepted. Now if you run form on your side, you can't set the cookie (as the cookie can be only set in domain of its origin).
If there is just some particular action you want to perform on their site, you can get away with browser automation. (i.e. your run browser on your server-side, script the action and execute it).
As for B) safest and smallest change is contradiction :) Smallest change would be to create handler for POST request on their side where you'll send username and password (this handler HAS TO run over https) and it will create auth cookie on their side.
As for safest - the whole concept of storing encrypted (not hashed) passwords is questionable at best (would you like your site to be listed here http://plaintextoffenders.com/ ?). Also if user changes his password on their side you're screwed. Secure solution would be that you'll store just 3pty UserID on your side, and you'll send asymmetrically encrypted UserID with Timestamp to their side (you'll encrypt it with your private key). They'll decrypt it (they'll have to have public key), validate if timestamp is not to old and if not they'll create auth cookie for given user id. There are also protocols for that (like SAML).
A)
What you are trying to do is really a form of a CSRF attack.
The idea behind a cross-site request forgery attack is that an attacker tricks a browser into performing an action as a user on some site, as the user who is using the site. The user is usually identified by a session identifier stored in a cookie, and cookies are sent along automatically. This means that without protection, an attacker would be able to perform actions on the target site.
To prevent CSRF, a site typically includes an anti-CSRF token in pages, which is tied to the session and is sent along in requests made from the legitimate site.
This works because the token is unpredictable, and the attacker cannot read the token value from the legitimate site's pages.
I could list various ways in which CSRF protection may be bypassed, but these all depend on on an incorrect implementation of the anti-CSRF mechanism. If you manage to do so, you have found a security vulnerability in theirsite.com.
For more background information about CSRF, see https://www.owasp.org/index.php/Cross-Site_Request_Forgery_(CSRF).
B)
The smallest change which theirsite.com could do is to disable the CSRF protection check for the login page.
CSRF protection depends on the unpredictability of requests, and for login pages, the secret password itself protects against CSRF. An extra check through an anti-CSRF token is unnecessary.
I'm finding myself in a situation where I could provide a much nicer user experience if I could disable CSRF token checking for an endpoint in my rails app.
The endpoint is a create action (routed to by POST /whatever), that's behind a devise :authenticate! filter.
Would I open myself up to any additional security risks by disabling the CSRF-protection for that specific endpoint, or can I safely rely on the authentication before_filter to stop the kind of malicious requests that the CSRF token protects against?
Following is a bit more detailed explanation as to why I want to do this if anyone is interested.
My use case is that I basically want to create something very similar to the Facebook likebutton, but this button (unlike the Facebook counterpart) is commonly going to occur multiple times on the same page.
The CSRF protection works fine except for the case where the user visits the page with empty cookies.
In this case rails generates a new session for each of the X number of requests since they are all cookie-less. And, of course, for each new session a new CSRF token is generated and returned in the response to the iframe.
Since the browser only keeps one cookie for the domain, any subsequent requests from each of the iframes will be mapped to the same session, and thus all of the CSRF tokens (except one) are invalid.
The mapping to a single session is nice since the user can be prompted to log in once, and then be mapped to the same log in for each of the subsequent buttons presses – without having to reload the page.
A compromise would be to respond with a 401 Unauthorized, but preserve the session of the rejected request (by overriding handle_unverified_request). This would trigger the sign in popup again, but this time an instant redirect occurs since the user is already signed in.
It would, of course, be best to avoid that flash of the sign in popup window, and thus I'd like to disable the CSRF protection all together for just the create action.
Authenticated requests are precisely what CSRF is about.
What CSRF means is that the attacker convinces the user's browser to make a request. For example you visit a page hosted by an attacker that has a form that looks like
<form action="http://www.yourapp.com/some_action">
#for parameters here
</action>
And some javascript on the page that auto submits the form. If the user is already logged in to your app, then this request will pass any cookie based authentication checks. However the attacker doesn't know the csrf token.
For an unauthenticated request, csrf serves no purpose - the attacker can just go ahead and make the request anyway - they don't need to hijack the victim's credentials.
So, short version: disabling csrf protection will leave you vulnerable to csrf style attacks.
What CSRF is really about is making sure the form contains a parameter that an attacker can't fake. The session is an easy place to store such a value but I imagine you could come up with alternatives. For example if the user can't control any of the parameters in the form, you could add another parameter which would be a signature of all the other parameters in the form (possibly with some sort of timestamp or nonce to prevent replay attacks). Upon receiving the request you can tell whether the request is from a form you generated by verifying the signature.
Be very careful about this sort of stuff as it is easy to get wrong (and even the big boys get it wrong sometimes.
Is it possible to login with net/http or curl, save the cookie response then write that cookie to the users browser so I can "push login" (in a sense)?
Is there another way of doing this, is this even possible, or am I simply crazy?
I'm aware of how I can login with net/http save the cookie and do things serverside.
I just don't want to spend the time saving the cookie to the database and then writing it to a browser and when successful redirect the browser if this isn't possible because of security restrictions.
You can only write cookies for the current domain - so if you're wishing to effectively connect to domainb.com from domaina.com over net/http, read the cookie returned from domainb.com and preset that for the user before redirecting them to domainb.com, then that won't work.
However, if you simply want to read the returned cookies from domainb.com and effectively duplicate them on domaina.com then there is no reason you can't do that. Something like:
# perform your request...
# once the response has been returned, loop through each cookie
response.get_fields('set-cookie').each do |cookie|
# set your local cookie here
end
If you want to implement automatic login between 2 domains, the best way I've found to do it is:
1) domaina.com says "hey I've got user ABC here, can I have an auth token for them?" to domainb.com's API.
2) domainb.com's API creates a token for that user, limiting expiry to say 1 minute, then sends that as a response to domaina.com
3) domaina.com redirects the user to domainb.com with the authentication token passed as a parameter
4) domainb.com receives the user's request (from the redirect), looks up the token in the DB, then automatically logs the user in, if it's found
Of course that does require that you control the systems of both domains... if you don't the likelihood is that oauth will be your best mechanism of cross-site authentication.
This sounds like a good fit for authentication_token based login, where as long as the link is correct (e.g. http://www.example.com/posts/new?auth_token=1asdfj2828728we924834), the user is auto-logged in. Check out Devise: http://www.hyperionreactor.net/blog/token-based-authentication-rails-3-and-rails-2
I'm fairly new to Rails and I'm trying to implement a really basic user authentication system. My main problem right now is that I do not have a clue what's a good design for this.
What I have now
My Rails app is not a website; it's a webservice that communicates with an iPhone app. All communication is in JSON.
When a user signs up, the app sends a POST request to /users.json with name and password as JSON in the HTTP body. Rails then saves the user id in a cookie:
cookies.signed[:user_id] = #user.id
When a user logs out, the app sends a POST request to /logout.json. Rails gets the user id from the cookie, updates the database and deletes the cookie.
Later, when the user wants to log in again, the app sends a POST request to /login.json with name and password as JSON in the HTTP body. Rails sets the cookie again.
My question(s)
Is this design RESTful? Because login and logout aren't resources.
Is it secure to use cookies like this? I'm planning to use SSL.
Is there a better way to do this?
Tt's a very basic approach
A more RESTful way:
Create a controller called sessions with a create and destroy action. Throw the login/logout out of your head and start thinking in sessions. A login is just a username/password combination. When you login you create/start a session, when you log out you destroy the session. The login is not affected.
Instead of using cookies.signed[:user_id] you should use session[:user_id]
SSL is a big plus, because the password can't be sniffed through the network.
And there are lots of out-of-the-box authentication systems.
My favourite is Devise:
https://github.com/plataformatec/devise
http://asciicasts.com/episodes/209-introducing-devise
http://railscasts.com/episodes/209-introducing-devise
there is a nice screencast ryan bates did on authentication from scratch
http://railscasts.com/episodes/250-authentication-from-scratch
No, for the reason you stated.
No, you need to pass a second parameter to encrypt the user's id.
Well, you could use a gem, there's a few about.
If you do want to code it yourself, then I'd recommend looking at a tutorial. The one at railstutorial.org is pretty good.
The basic idea is to create a user model with an encrypted password, which uses a salt formed from the current time and password. You then create a sessions controller and make it RESTful, but using cookies instead of another resource. Make sure to encrypt the cookie with the salt as well:
cookies.permanent.signed[:remember_token] = [user.id, user.salt]
Creating RSS feed in rails is easy. I need a simple way to password protect the RSS feed. I am thinking http basic authentication.
I googled but could not find any article about creating password protected RSS.
I have this in my ApplicationController
def xml_authorize
if request.format == Mime::XML
authenticate_or_request_with_http_basic do |username, password|
username == 'foo' && password == 'bar'
end
end
end
Then I just apply a before_filter :xml_authorize to the actions that I want to password protect for XML requests only, but still want to serve normally with html.
Here's where I got the inspiration from.
Just use whatever auth system you use on your regular controllers. If the user is logged, and session is alive he will be able to download the feed.
How is HTTP authentication any different on RSS feeds than other controller actions? (This is not necessarily a rhetorical question - is it actually different?)
Have you tried just using the Rails HTTP Basic Authentication methods?
Feeds are meant to be fetched in regular intervals without user interaction.
RSS-Feeds can be consumed by something different than a browser. For example,
I have a phone where I can create a widget for the start screen from a rss-feed-link. Great function. Unfortunately authentication does not work for this widget.
And there is no user interface to put in username and password. So the authentication need to be part of the url, with all the bad security implications...
On the other hand you don't need a session to answer a feed-request.
So the solution is a create a special key for the user, and store it in the user table.
Then use it when you display the link to the rss-feed. In the feed-method, you use this key to retrieve the user, but you don't create a session. This way, even when a hacker somehow got a valid key, the hacker can only view the rss-feed, but not access the rest of your application.
If you already use some library for authentication, there may already some solution implemented for this. In Authlogic, is is the class SingleAccessToken, and you need to add a column 'single_access_token' of type string to your user table. Then authlogic creates some cryptic key when are saving the user record. You than add this key as the GET-Parameter 'user_credentials' to the url of the private rss-feed
Like knoopx said, If you use an authentication system like authlogic you should be able to specify the authentication type in the controller. You can then specify http basic authenication. Then you can, if you choose, include the authentication in the URL for the RSS Feed.
e.g:
http://username:password#example.com/rss
(sorry to break the URI up like that, but I don't have enough reputation points to post multiple links :( )