As you've identified, the difference between 2.3.10 and 2.3.11, and
between 3.0.3 and 3.0.4, in how they handle invalid csrf tokens is that
the former will raise ActionController::InvalidAuthenticityToken, but
the latter will reset the session.
What we are trying to protect against is the following situation:
* Alice is logged in to Facebook
* Alice visits badsite.com
* Mallory, who owns badsite.com has added some code into the page which
makes a request to facebook.com and posts on Alice's wall.
* Alice visits badsite.com and without her intending it to be, a comment
is posted on her wall
With the current CSRF protection, the following will happen:
* Alice is logged in to Facebook
* Alice visits badsite.com
* Mallory, who owns badsite.com has added some code into the page which
makes a request to facebook.com and posts on Alice's wall.
* Alice visits badsite.com and without her intending it to be, a request
is made to post on her wall
* Facebook detects that there is no CSRF token associated with the
request, and so logs her out by resetting the session
* Then, based on the authorisation rules within the application,
Facebook denies to post on the wall, because the user is not logged in
With the old CSRF protection, the following will happen:
* Alice is logged in to Facebook
* Alice visits badsite.com
* Mallory, who owns badsite.com has added some code into the page which
makes a request to facebook.com and posts on Alice's wall.
* Alice visits badsite.com and without her intending it to be, a request
is made to post on her wall
* Facebook detects that there is no CSRF token associated with the
request and so refuses to serve the request in any way (returns 500)
* So nothing gets posted on the wall
The point is, they are different but both have the effect of preventing
the wall post.
If for some reason you specifically want an exception to be raised in
this situation, you can customise handle_unverified_request, but it
doesn't compromise the aim of the CSRF protection to no raise an
exception, so long as the request is not allowed to go through as
authenticated by the existing session.
Jon
I have a question.
> With the current CSRF protection, the following will happen:
>
> * Alice is logged in to Facebook
> * Alice visits badsite.com
> * Mallory, who owns badsite.com has added some code into the page which
> makes a request to facebook.com and posts on Alice's wall.
> * Alice visits badsite.com and without her intending it to be, a request
> is made to post on her wall
> * Facebook detects that there is no CSRF token associated with the
> request, and so logs her out by resetting the session
> * Then, based on the authorisation rules within the application,
> Facebook denies to post on the wall, because the user is not logged in
In my understanding, Alice gets logged out from Facebook unintentionally
by visiting badsite.com. I think this is an ugly side effect. Am I wrong?
CSRF attacks are about using *session* data to perform an action
without the user's knowledge. The attack you're describing here,
which doesn't rely on session data, could also be performed just using
curl from the command line of the attackers laptop, there's no need to
involve the user's computer at all.
If there's no involvement of the session (or some other
client-browser-specific) vector, then it's not a CSRF attack.
However you're correct that we need a little more documentation than
we had previously because the attack vectors are a touch trickier. I
intend to post an FAQ blog post and update the guides in a few days to
ensure that it's crystal clear what's required.
The thing to remember is that we *can't* raise an exception in the
default case any more because *every api request would be rejected*.
For users who don't have any API, then the original behaviour was
probably fine. However a large portion of rails apps do and we can't
completely break them!
> I hope you see my point.
I see your point and agree that we need a little more documentation
around this. However it is nowhere near as simple as your suggestions
imply. Developers will have to understand the implications of their
choices.
I suspect we'll end up with an api like:
protect_from_forgery :on_failure=>:raise
protect_from_forgery :on_failure=>:reset
protect_from_forgery :on_failure=>:some_method
protect_from_forgery :on_failure=> proc { ... }
I realise that the previous situation where you could just ignore the
issue was greatly preferable, but that was based on some assumptions
which don't hold. The old rules don't apply anymore.
> --
> You received this message because you are subscribed to the Google Groups "Ruby on Rails: Core" group.
> To post to this group, send email to rubyonra...@googlegroups.com.
> To unsubscribe from this group, send email to rubyonrails-co...@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/rubyonrails-core?hl=en.
>
>
--
Cheers
Koz
So you're saying people with dynamic IPs which rotate every 12 hours
(the majority of Uruguay, for example) should just log in into your
site each time the IP rotates, or am I reading it wrong?
The security didn't get loosened, it got changed in a manner that
preserves security for the bulk of use cases while preserving the
restful API support that so many people rely on.
> As mentioned, I think starting a new sessions from a "clean" IP
> address counts as client-browser specific.
IP addresses are cheap and plentiful and don't provide the security
you seem to think they do. For example the entire nation of qatar
makes internet requests from the same IP address. Would your
theoretical site only accept a single vote from qatar.
However that's beside the point, your application could either
override the default handler or require users view the poll question
before voting (making use of the session). Both are perfectly
acceptable answers, what's not acceptable would be rejecting each and
every API request to every rails app on the internet.
> I don't really see why. The old system was easy to bypass for specific
> controllers and actions, while the default was safe for all abuse-
> cases. The new system is aimed towards applications with an api that
> don't want to use skip_before_filter and that do use a session for
> their security-measures.
> What assumption does not hold anymore? Which rule changed?
I think you missed the details of the security vulnerability, our
previous method of whitelisting API requests has been broken.
Previously your API requests *didn't trigger CSRF protection*, now
they do. *that* is why the error handling had to change. Previously
we could raise 500 errors because the checking never fired for API
requests. Your entire model of using seperate api controllers for
CSRF protection was completely unnecessary under the old system.
Please take the time to read the advisory carefully as I think you're
misunderstanding why we undertook this change. Attackers can forge
arbitrary HTTP headers, previously this was not believed to be
possible. Because of this incorrect assumption (made by basically the
entire web security community, not just us) we were able to whitelist
certain requests. We can no longer whitelist anything, everything can
be forged.
It was not undertaken lightly and it was not done without a lot of
careful consideration. We consulted with security experts, analysed
various alternatives and chose the least-worse option out of a series
of bad options.
The setting as it is now will not be changed, we may enhance the api
in the future and we will improve the documentation to make sure
people can make informed choices. But the default won't change.
--
Cheers
Koz
on our homepage we
have a login form. Whether this login form has the authentication
token or not doesn't matter anymore, login always succeeds. Even with
cookies disabled. Previously it would not.
How is this a security flaw? Login only succeeds if the credentials are correct. If someone has credientials, they can login to the site, and I don't see what role forged cross-site requests play in this case.