[Play 2.5.3] CSRF + CORS How to deal with ?

1,620 views
Skip to first unread message

Thibault Meyer

unread,
Jun 2, 2016, 11:28:07 AM6/2/16
to play-framework
Hi,

We using the global CSRF with CORS settings to bypass some settings. But we can't request our API with cURL because, even if CORS alors /api/*, the CSRF filter still annoying us with the CSRF token.

It's seem CORS filter dont do its job !

ane idea how to disabled CSRF for all method belong the /api path ?


POST /api/ping  with header Authorization: Bearer xxxxxxxx
403 : No CSRF token found in headers


GET /api/ping  with header Authorization: Bearer xxxxxxxx
200: OK


play.filters {
 cors
{
   pathPrefixes
= ["/api", "/callback"]
 
}

 csrf
{
   
# Sets the cookie to be sent only over HTTPS
   
#cookie.secure = true

   
# Defaults to CSRFErrorHandler in the root package.
   
#errorHandler = MyCSRFErrorHandler
 
}
}

Greg Methvin

unread,
Jun 2, 2016, 3:22:26 PM6/2/16
to play-framework
Is your CORS filter applied before the CSRF filter? The CORS filter will tag the request, and by default, the CSRF filter will let through requests from trusted origins. There are also several other settings which may allow you to identify an API request: https://www.playframework.com/documentation/2.5.x/ScalaCsrf#Plays-CSRF-protection

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/play-framework/eb4fa884-2eb3-4695-837f-e7b9eb1f0b9c%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.



--
Greg Methvin
Senior Software Engineer

Thibault Meyer

unread,
Jun 2, 2016, 3:44:44 PM6/2/16
to play-framework
Hi Greg,

Yes, the CORS filter is applied before the CSRF.  After some tests, it appear that "allow all origins"  (when allowedOrigins=null) is not equal to "no origin provided"


We have run POST requests with success by adding "Origin: fake://nothing" header on each request.

Maybe is an issue ? If allowedOrigins is null and header Origin not provided, the request should be accepted.


This is problematic our payment providers. We can't ask them to add an "Origin" header on their call Callback calls.

Greg Methvin

unread,
Jun 2, 2016, 4:54:07 PM6/2/16
to play-framework
The CORS filter only applies to CORS requests, and CORS requests must have an Origin header. That's why you're seeing the behavior you describe.

I'm not sure how your API requests differ from your regular web requests, but you could find some other way to distinguish the two and not apply the CSRF filter there. Perhaps using the content type whitelist? (https://github.com/playframework/playframework/blob/2.5.3/framework/src/play-filters-helpers/src/main/resources/reference.conf#L78). There's no way to filter the path using configuration in the CSRF filter, but you can always wrap it with your own filter, or use action composition with CSRF actions instead of a global filter.

Greg


For more options, visit https://groups.google.com/d/optout.
Message has been deleted

Thibault Meyer

unread,
Jun 3, 2016, 7:39:47 AM6/3/16
to play-framework
Ok thanks,

we have developped a module based on CORS filter to allow "trusted path" (ie: /api/ , /callback/).

But we are now an issue with JQUERY and the CORS filter.  Why we bypass CORS allowedOrigins when the Origin is equal to the Host ? Now wa can process JQuery request from jsfiddle  POST https://www.test.com/dashboard/upgrade-settings) but we can't process POST request from the same project (ie: POST /dashboard/upgrade-settings)

Maybe "isSameOrigin" method should be optionnal (ie: a new configuration key: ignoreSameOrigin = false) or juste removed ?

Greg Methvin

unread,
Jun 3, 2016, 10:00:27 PM6/3/16
to play-framework
On Fri, Jun 3, 2016 at 4:39 AM, Thibault Meyer <thibaul...@payintech.com> wrote:
But we are now an issue with JQUERY and the CORS filter.  Why we bypass CORS allowedOrigins when the Origin is equal to the Host ? Now wa can process JQuery request from jsfiddle  POST https://www.test.com/dashboard/upgrade-settings) but we can't process POST request from the same project (ie: POST /dashboard/upgrade-settings)

The CORS filter is responsible for sending the CORS headers when they are required. If it's the same origin, it doesn't need to do anything because we trust our own origin, and the browser doesn't expect CORS headers in that case.
 
Maybe "isSameOrigin" method should be optionnal (ie: a new configuration key: ignoreSameOrigin = false) or juste removed ?

You want the CORS filter to tag same-origin requests as "trusted", so the CSRF filter will let them through? That is safe to do, but I don't think the check belongs in the CORS filter since it has nothing to do with CORS. I would prefer to do a same-origin check in the CSRF filter, and add a config like play.filters.csrf.trustSameOrigin = true.

Unfortunately many browsers, including Firefox, do not send the Origin header on same-origin requests. So you would still need to generate and check the CSRF token if you want to support them. If we added the option, it should be probably disabled by default and have a warning about that in the reference.conf and documentation.

Thibault Meyer

unread,
Jun 4, 2016, 2:50:40 AM6/4/16
to play-framework
Hi Greg,

I got it. thanks for this response. So is it recommanded to retrieve the CSRF token from hidden field and use it in AJAX POST queries.

But how many time the same CSRF token can be used before it became refused by the server ? If this case happen, do you have any advices to retrieve a new valid token without refresh the entire page ?


Sincerly.

Greg Methvin

unread,
Jun 4, 2016, 3:43:55 AM6/4/16
to play-framework
It should work as long as the CSRF cookie is set. If you are server rendering a page and then sending ajax requests with jQuery, you could do something like:

var CSRF_TOKEN = "@{play.filters.csrf.CSRF.getToken(request)}";
$("body").bind("ajaxSend", function(event, xhr, settings) {
  if (settings.type == "POST") {
    xhr.setRequestHeader('CSRF-Token', CSRF_TOKEN);
  }
});

That will add the header to all POST ajax requests.

--
You received this message because you are subscribed to the Google Groups "play-framework" group.
To unsubscribe from this group and stop receiving emails from it, send an email to play-framewor...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
0 new messages