I'm using Symfony2 and protecting my forms with a CSRF token.
I have a comments system based on Ajax calls. If a user wants to edit his comment, here's what's happening:
A user hits the edit button.
A "fresh" comment edit form is loaded via ajax.
The user edit and submit the form via ajax.
The edited comment is sent back in response.
Is loading the "fresh" edit form via ajax a security risk?
If the form were already in the loaded page and couldn't be requested via ajax, an attacker could not guess the CSRF Token, but since he can request the form he can get his hands on the Token..
Couldn't he..?
Maybe an example will make it clearer:
Dave is an innocent registered user in my site (www.acme.com).
Dave logged in my site and then visited www.evil.com. He doesn't know that, but when he visited evil.com a script was executed.
The script sent an ajax request to www.acme.com/comments/123/edit and got the edit form in response.
It then filled in that form with it's malicious content and submitted that form (again, with ajax).
Will evil's evil plan work?
As far as i understand, there is no risk if your form contains CSRF token field. Default Symfony2 CSRF token depends on session which is not availiable for the attacker (and also on intention). So when the attacker requests the form there is attacker's (not user's) session id used.
Related
Currently, I’m developing a web app with server-side rendering using the Gin framework and I’m having a problem with login intercepting. When an HTTP GET request hits an endpoint, middleware is used to check the browser cookie and redirect the traffic to the login page. This works fine and after successful login, the user is always redirected to the dashboard page. My question is how I should redirect the user back to the originally requested URI instead of the dashboard page?
Also, a bit more complex scenario is on HTTP POST. It looks like the HTTP POST method doesn’t work quite well with a redirect. Also, how would I resume the request with the same post request after the user successfully login?
Thanks for the help!
For the HTTP GET scenario, this one is easy, you need to remember the original URL somewhere. The are a few ways you could go about this:
Store the URL in session information(if any is available, you do need sessions for non-authenticated users)
Store it in a query string, for example, redirect to example.com/login?original=https%3A%2F%2Fexample.com%2Fanother-page. Your login page can look for the query parameter and include it in the login form or make sure that the action of the login form matches the given URI. On a successful login attempt you can get the original URL form the query param and set it as the Location.
Store the original URL in a cookie, upon successful login you can just check the cookie value and use that.
As for the HTTP POST scenario. If you just want to redirect the same POST request to a different URL you can use a 307 Temporary redirect. A 307 will preserve the request body and method and not turn it into a GET request like a 303 See Other or 302 Found.
Resuming the original POST after showing the login screen and after a successful login is a little more complex. When you redirect to the login page you interrupt the flow of the user, maybe it is better to let the user re-post their request after logging in, instead of doing it for them.
Having said that, it is technically possible. We require two steps, first is storing all the data to recreate the request. Then after login completion we can render a form with this saved data and use javascript to submit the form. By adding:
<script>document.getElementById("myForm").submit();</script>
After your form, the browser will submit the form after loading the javascript, thus recreating the original POST.
The storage part can be done via the server side session or a cookie.
I'm trying to pass a CSRF token in a form that is submitted via a javascript (not jquery) AJAX function. The accepted wisdom appears to be to include the token as a hidden input in the actual form. As I see it the problem with this is that the contents of a hidden input can easily be seen using a browsers inspect facility. So is there a more secure way to pass the token?
It's not an issue that the CSRF token is accessible in the source code because it's not meant to be hidden to the client browser. And when I say "hidden" here I am not talking about the HTML property "hidden" of a form, but the disclosure of the token, should it be by analyzing page source, script execution or network traffic (really hidden).
You must understand why CSRF tokens are useful. If an attacker creates a malicious webpage, hosted under an external domain, which POST or GET to your website, then he can expect an authenticated user (a victim - with an open session and sesion ID in cookie) to perform the POST/GET: the victim's browser will detect the target URL, add the cookies/session ID to the GET/POST headers, and perform the action on behalf of the authenticated user (eg: "destroy my account").
If you create a random CSRF token in the source, the attacker cannot read it (because he cannot load the page on behalf of the victim's browser and read its content, thanks to Cross-Domain content segregation) and hence cannot build a malicious page which will perform a GET or POST.
As for other methods, other websites use a unique static CSRF token (generated per user on session init), which is saved in cookies and included in the forms of the sites through JS. The result is the same, the token will be part of the form submit (and so accessible to the client's browser). You just avoid producing CSRF tokens for each form and the server can easily compare the token with the client's session data rather than performing side-channel token management.
I'm trying to better understand the mechanism for how Spring CSRF protection works. Suppose I have a site https://example.com/ where people can vote on candidates. Users can also exchange messages. I also have a user logged in, and another user that sends her a message saying to click on the link https://example.com/vote/candiate/30.
If a user clicks on this link, won't the browser send both the CSRF token and the session ID for the logged in user, thereby bypassing the CSRF protection check?
The reason a link is usually not a problem regarding CSRF is that CSRF is only an issue when the request changes something. A link (a GET request) should not change anything. If it does, like in your example it adds a vote to the candidate I suppose, any link from an external origin (a different website) would also be able to exploit "normal" CSRF by just linking to that url.
The problem in the example is not that CSRF protection is inadequate in Spring, the problem is that voting in this case is a GET request, and GETs are not usually protected against CSRF by design. The solution is to change the vote request to a POST, which would then be protected against CSRF (and which would also be more RESTful btw).
Main idea :
When request is submitted, the server received special cookie and waits for defined value in this cookie. If this value will be differet , the request should fail.
So, if service returns form for moving money between accounts, this form includes parameter, that expected to receive when form is submitted, and if data would be sent without this parameter, request wouldn't be proccessed
I have a login form submitted with Ajax. In one specific case, after the user logs in, I need to log them out with Auth::logout() and display an additional modal box. All of this happens with no page reload.
When the login modal is opened and submitted again, I get a Token mismatch error. The reason why this happens is because the logout uses Session::flush(). After this the _token Session variable is refreshed, while the _token input in the login form stays the same (because of the mentioned lack of page reload).
How can I refresh the CSRF _token in the login form, so it matches the one refreshed in the Session in a secure way?
When your login is submitted via ajax, your server is presumably sending back some kind of response to let the browser know the login was successful. You should send back the new CSRF token with this response, so that you can update the form client-side (with javascript).
To provide any more detail (how to update form fields, for example) we would need to see more of your code.
I have a page on domain A which includes a javascript from from domain B. The script loads a form from domain A with Ajax and posts it back to A.
The form got rejected by Yesod because of missing session variable which resides in a cookie and isn't transmitted on Ajax request because of that.
Can Yesod's session mechanism be made work in such a situation?
I was given an answer by Michael Shoyman, the author of Yesod. The easiest way in my case is to disable CSRF protection for that particular form. There is an api function for that.
http://hackage.haskell.org/packages/archive/yesod-form/1.1.4.1/doc/html/Yesod-Form-Functions.html#v:runFormPostNoToken