I'm designing a CSRF defense in a Grails 2.4.5 application.
I would like to use the Synchronizer Tokens pattern, and here is the design I intend, simplified:
With a new session, on the server, generate a long unique csrf token.
Store the csrf token in the session.
Add the csrf token to the response header
On the server, for every incoming request that will potentially change application state (post, update, delete...) validate that the referer/origin is the same as the target
On the server, for every incoming request that will potentially change application state (post, update, delete...) check the presence and validity of the csrf token in the header.
In a grails app, I imagine this taking place in the Spring Security filterchain.
If the token is not present or does not match, return an error and log a potential CSRF attack, otherwise allow the request to proceed.
Does this sound correct?
My concern is that it's not sufficient. Officially, the synchronizer token is added to every form and every post, put, or delete URL. Yet, the application doesn't use form tags, so it seems to be onerous to add it to every single JavaScript-generated form in the application, not to mention requiring extensive testing to make sure no existing functionality is broken.
Thanks!
Related
It seems to me that the primary goal of CSRF is to confirm that the client making the request is the client we expect.
The solution I've commonly seen is:
Server generates random CSRF Token
Server sets CSRF token in cookie
Server injects the CSRF token into the form when generating the form
OR
Server passes the CSRF token to javascript and javascript injects the CSRF token as a header on XMLHTTPRequests
When a request is received, it's validated by checking that the CSRF token in the cookie matches the CSRF token in header/form value.
It makes a lot of sense to me that the server is generating the CSRF for (3)(1), but I cannot come up with a reason why it's necessary for (3)(2).
Instead, if the client is pure javascript, I believe this is safe:
Javascript generates a random CSRF token
Javascript sets the CSRF token in a cookie
Javascript passes the CSRF token in the header when making an XMLHTTPRequest
Server checks that CSRF token in the header and cookie match
My understanding is that 3 and 4 are both things an attacker cannot do, so this would also sufficiently block attacks. Is that correct?
If that is safe, do we even need to do step (1) and (2)? Would this be safe as well because of same-origin policy (assuming cors is configured properly)?
Javascript sets an 'CSRF-Safe: true' header in XMLHTTPRequest
Server checks that the header CSRF-Safe exists and is set to "true"
Yes, both of those simplified approaches should be safe in the presence of CORS and the same-origin policy. In fact, you don't even need the CSRF-Safe: true header as long as you validate the content-type.
Wikipedia confirms:
If data is sent in any other format (JSON, XML) a standard method is to issue a POST request using XMLHttpRequest with CSRF attacks prevented by SOP and CORS; there is a technique to send arbitrary content from a simple HTML form using ENCTYPE attribute; such a fake request can be distinguished from legitimate ones by text/plain content type, but if this is not enforced on the server, CSRF can be executed[12][13]
I'm new to the cybersecurity and CSRF.
I've read the most popular way of the CSRF-protection is placing the CSRF-token in HTML form or in the META tag for reading it in JavaScript for AJAX-actions.
But I don't understand what the sense of the hacker who use data from cookies at different sites can read the value of the CSRF token in HTML and use it sending a request to the site where the user logged in.
Explain it for me, please.
CSRF Token are invented to prevent actions on the server from outside of the ecosystem.
In the web world, prevent posting a form (it can be any other action on the server) from out of the original site.
You are asking how, so the simple technique to put hidden field inside the form with some generated token that the server can ensure that the Post request that it got came from the site's form and not from other place.
For example, if I'm an attacker and your form doesn't has such a token I can create a form on my site that the action field will point to you server location.
<form method="POST" action="http://your-site.com/transfer-mony.php">
<input type="text" name="amount" value="100000" />
<button>submit</button>
</form>
If you have a token and you server will validate it on each request, my post from my form will be rejected.
So you asking, OK, I can go to the original site and copy that token, and that is it.
So, basically, token should be a one time thing, they valid only for the next user action.
Token can contain an user IP, and signed with private key, then if you copy my token to you, the server will reject the request, because the IP that within the token is not aligned with the IP of the client that sent the request.
JWT, is one of those techniques
The more common practice today (thanks to Angular), is that the server return a Cookie with the token, and the next request must have it on the Headers of the request.
Pay attention, you your site has an XSS vulnerability you can basically bypass all CSRF mechanisms.
I hope it was understandable, if not, you can ask at the comments.
I am working on Java application . Front end would be Angular2 .
If I try to open my application home page( index.html is configured in web.xml as default page ) . Access URL should be http://localhost:8080/MyWebApp .
Then I have taken into an standard organization's login page for authentication. If authentication succes , HTTP Authorization token will be set in the request header and finally control comes to display my application home page.
If I use jsp, I can get request header as,
String authHeader = request.getHeader("authorization");
out.println("<h2>HTTP Authorization header:</h2>");
if (authHeader == null) {
out.print("No authorization header");
} else {
out.print("<textarea readonly id='authHeader' rows=\"5\" cols=\"80\">" + authHeader + "</textarea>");
}
But we are using html as front end, because of angular 2 .
So for my scenario, how I can I get the request header and token.
Please don't hesitate to edit my question, if it is not clear.
You can't get a value of a header from client-side JavaScript. The only exceptions are the User-Agent and Referrer headers, because the browser provides the values in the document and navigator objects.
You said you are working on a Java application with an Angular 2 front end and some other application provides a token (might be useful to specify if this is something standard, e.g. OAuth2). I will assume that it is a custom token. I believe you also meant you have some server side component, a servlet.
What you can do is to implement the authentication using the servlets (or even JSPs) and then redirect back to the Angular 2 front end application, passing the token in the URL as a query parameter. URL is easy to read in Angular 2. However this is not very secure, even if you use something like JWT. As an alternative to URL, you can use the Set-Cookie header and then read the cookie from Angular.
What would be almost secure would be to authenticate the user using the server side (servlet or even JSP). Then create a one-time token which is passed in the URL as a query parameter when redirecting to your HTML page. Then use the one-time token in a call to the server again to retrieve the real authentication token using a proper REST call from Angular 2 with request and response.
Depends on how much control you have and what kind of authentication the auth application uses, you might want to take a look at the OAuth2. It deals with plenty of different authentication scenarios. Specifically the OAuth2 implicit grant flow is used to authenticate users from client-side only applications. Even if you can't use that, it will give you some ideas.
When you are using a server-side authorization, your server put headers with authorization to your HTML pages. But also you can put this tokens to your page response by meta tags at server side. And then access to meta tags by js.
<meta name="_csrf" content="${_csrf.token}"/>
<meta name="_csrf_header" content="${_csrf.headerName}"/>
Meta tags are similar to response headers and can complete or override response headers.
Read this post please Spring Security CSRF Token not working with AJAX call & form submit in same JSP
You can handle this at server side(JSP's expressions work on server side), create a handler method on server where you can check header and then redirect to your Angular App.
I think we can use HTTP HEAD method as JQUERY AJAX request in your HTML page .
https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol
The HEAD method asks for a response identical to that of a GET request, but without the response body. This is useful for retrieving meta-information written in response headers, without having to transport the entire content.
ajaxRequest = $.ajax({
type: "HEAD",
async: true,
url: 'index.jsp',
success: function(message){
var headerValue =ajaxRequest.getResponseHeader('Content-Length')]);
}
});
There are various way to solve this issue as I faced it a lot before and what I prefer is;
When authentication is completed in login page and a token generates I store it into HTML storage, make be in localStorage.
The main point you should understand that your views should not be accessed directly and there has to be a authentication (or may be a authorisation) step(s) before accessing the page(view).
So what you can do is set a URI for accessing any page, consider that is;
http://host/appname/pageName
And when you connect with this URI via ajax call add the token that is stored in localStorage in headers. And check all authentication and authorisation works and if success return the view(as the pageName suggested in the URI), else return the login view.
If i understand you correctly,
Angularjs is a client side framework and is intended to run inside a browser without any intervention of server by reducing its load by serving the application logic
All operations that need to be performed by angular will only be initiated at client side by the browser after loading the HTML and javascript.
The scope of angular is only limited to that area any way it is not a disadvantage it is the actual intention of client side frameworks.
Regarding request response headers you can only have access to headers of AJAX request
Following are the solutions to these problems:-
If you are using tomcat or any servelet container in order to serve the application or hosting angular code you can use JSP insted of HTML,since JSP is processed to html by the servelet container before passing it to client side.I think this solution will work in your case based on my inference form your question
Otherwise configure servelet that process the success and failure handlers from the authentication server and from angular you need to poll the servelet for getting the request header value.
I have just learned about JWT for authentification. Storing the JWT token in localStorage/sessionStorage is exposed to XSS. Storing it in a cookie is vulnerable to CSRF. I have been researching this and I thought of this solution, but I'm not sure how secure is this and if I should be using it in production.
The soulution is to get the JWT token from server, store it in cookie. Generate a CSRF token (that will be stored in JWT) that will be sent with each HTML page, either in a hidden HTML field of as a global JS variable (). That CSRF token will be sent with every request using JS/AJAX. This way we can rule out CSRF first then verify the JWT token.
I'm not sure whether a new token should be sent with each loaded page, or a single token should be held per session. The 1st case would mean that only the last loaded page/form would be able to submit (which could be problematic if the user is having multiple tabs of other pages open).
Is this a secure solution that might be used in production ?
Also, what other solutions would be viable to reach the same goal ?
Here is my understanding of the issue.
JWT Token - stored in httponly/secure cookie - this ensures any Javascript does not have access to it
CSRF Token - stored by JS - on login, attach the claims as a header (including your CSRF value)
When the JS gets the header, it can store the claim in a cookie, localstorage, or session storage. Because no JS has access to the JWT token in the cookie, by attaching the CSRF with every request and making sure it matches what's in the JWT, you're ensuring that it's your JS sending the request and that it was your backend that issued the token.
A hidden field isn't a great solution because you'll have to add it to each request. Depending on what framework you're using, you should be able to load the CSRF token into whatever is responsible for sending the token as a header on each request when the page is refreshed. The benefit to having all the claims in local storage or a cookie is you can preserve the users front end state. Having the exp claim means you can tell when the user no longer has a valid token in the secure jwt token cookie and you can redirect to a login page.
sessionstorage - is specific to the tab only
localstorage - is specific to the domain across tabs
cookie - is specific to the domain across tabs
Add CSFR to AJAX request:
$.ajax({
type:"POST",
beforeSend: function (request)
{
request.setRequestHeader("CSRF-TOKEN", csrfToken);
},
url: "entities",
data: "json=" + escape(JSON.stringify(createRequestObject)),
processData: false,
success: function(msg) {
$("#results").append("The result =" + StringifyPretty(msg));
}
});
Even though you're not using Angular, this still applies. You should attach the token as a header with the JS and it's fine to get that value from a cookie.
From the Angular JS Documentation
When performing XHR requests, the $http service reads a token from a cookie (by default, XSRF-TOKEN) and sets it as an HTTP header (X-XSRF-TOKEN). Since only JavaScript that runs on your domain can read the cookie, your server can be assured that the XHR came from JavaScript running on your domain.
I'm getting a bit confused about how Hapi handles validation, according to the request lifecycle (http://hapijs.com/api#request-lifecycle) validation of params / queries only happens after authentication has been done.
Does it make sense to do so? For example, I'm working on a public API, but I do not want to go through all the authentication logic if the request params are invalid.
Say a request with the following validation:
validate: {
name: Joi.string().required()
}
Why would the API go through the trouble of authenticating the user when the request will be invalid due to name not being send?
Or is this me just misunderstanding the request lifecycle?
When validation fails, hapi returns information about why the validation failed. In your example, the route requires a parameter called name. For security reasons you might not want to tell an unauthenticated user that this route requires this parameter. That gives away a lot of information right?
For me personally I think there are a few reasons why authentication is done first:
What's more important, telling the user they are not authorized to access an endpoint, or that their request was invalid? I think authorization is more important.
For security reasons, don't reveal any more information than is absolutely necessary.
Once you establish your authentication method, the amount of "work" to authenticate a request is the same, but validation can vary a lot between requests. One route might have many parameters that need to be validated. So if you had to average out the cost of a failed request, authentication might be cheaper over many, many requests. (see JWT authentication)