I am trying to implement CSRF with Angular.js and CodeIgniter, however, I am not sure if my method is secure.
When Angular sends a post, put, or delete request, I have a custom security class in PHP check $_SERVER['HTTP_COOKIE'] from the request. If it includes the CSRF cookie, I verify it against the value in the $_COOKIE variable.
I am not sure if these are just always the same values and not actually doing anything or what. Is there a better way I should be handling this?
I have not worked with angular JS but with codeigniter. For csrf protection codeigniter provides security class for providing the security token(or the csrf cookie) for respective requests either via form. Now you only need to enable this feature under the config file:
Location: application/config/config.php
Default Code: $config['csrf_protection'] = FALSE;
Change it to: $config['csrf_protection'] = TRUE;
By making it true, it automatically inserts a security token in form, that is verified at the server end.
You can change the token name & cookie name in the same file via below config settings:
$config['csrf_token_name']
$config['csrf_cookie_name']
One important thing is that, if you refresh the page, security token doesn't change, or think it as same security token for current user session. This is somewhat drawback I have found with Codeigniter.
If you want same implementation with ajax, either you need to add manual code for passing the token name or value or use ajaxSetup for all requests.
By this way you can achieve the csrf protection at a level, if you want some more security then you can check for httponly cookies or ssl deployemnt subject to your project need.
Related
I have a server that stores session cookies and you can log onto it using a page (foo.com/login.html) that runs in the browser. The browser then stores a session cookie for this domain.
Now I want another page (bar.com) upon initialization to make a GET request using JavaScript to the first page (foo.com/authenticate) which should check if a session cookie exists in the browser and validate it, if correct he should respond with the session's username (however this is retrieved from the cookie). Of course I cannot check in bar.com's JavaScript if there exists a session cookie for foo.com.
Trying to solve this I ran into a few problems, one of which is of course CORS. I managed to avoid this problem by placing a reverse proxy in front of foo.com that adds all required CORS headers to the response. besides adding the headers, the proxy only tunnels requests through (eg. rev-proxy.com/authenticate -> foo.com/authenticate)
Now when I call the handler through the rev proxy from just another browser window directly (eg. rev-proxy.com/authenticate), I get the correct response. The handler from foo.com's backend finds the session cookie, reads out the username and passes it back. BUT when I try to make the same call from JavaScript inside bar.com (fetch("rev-proxy.com/authenticate")), I receive null, meaning he did not find the cookie (note that the request itself has status 200, meaning it did reach the backend of foo.com).
I have the feeling I am missing a crucial point in how cookies are used by browsers but I cannot find any useful information on my specific problem since I believe it is a rather unusual one.
See the MDN documentation:
fetch won’t send cookies, unless you set the credentials init option. (Since Aug 25, 2017. The spec changed the default credentials policy to same-origin. Firefox changed since 61.0b13.)
Generally, there are at least 2 options of securing applications using Keycloak OpenID Connect stack:
Use it on the application using Keycloak adapters (in my case, a SPA javascript front end)
Use it on Apache using mod_auth_openidc
If I choose to use number 2, how can I obtain the user data (username, for example)?
------------------------Edit due to #Cyril Dangerville answer -----------------
While I understand the general approach, my way is seemed very forced.
Now my authenticated, my request has session cookie and access token headers, but cannot access any header due to being initial page load
Perform an call to any protected resource to get access Header value
Use the access token to call the userinfo end point (But this is on another domain so the cookie wont get submitted)
Can you help me by explain where I should go here?
With mod_auth_openidc, you pass user data to applications as Apache environment variables and/or HTTP headers. The latter is useful if using Apache as reverse proxy for remote apps (mod_proxy). The proxy case is addressed briefly in mod_auth_openidc project's FAQ.
You can find more details about how to configure this translation of OpenID Connect token claims - including the authenticated user data - to environment variables/headers in the configuration file: auth_openidc.conf; two properties in particular:
For the REMOTE_USER variable: OIDCOAuthRemoteUserClaim <claim-name> [<regular-expression>]
For others: OIDCPassClaimsAs [none|headers|environment|both].
In our application we validate user name/password. Once validation is done, credentials are encoded using base64 and then needs to be set at request header for subsequent rest calls.
Need to set below in request header.
Authorization:Basic AQNLzR69OFTNJE8X
In the response setting as below from the java code,
javax.ws.rs.core.Response.status(200).entity("").header("Authorization:","Basic AQNLzR69OFTNJE8X").build();
And in the javascript tried setting as below,
sessionStorage.setItem('Authorization:', 'Basic AQNLzR69OFTNJE8X');
But in the subsequent rest service calls in the same session can see the header request is not set with authorization. Request to provide some pointers on setting the Authorization in javascript, so that it is retained for the entire session.
I think you misunderstand how authentication works (or should work).
You are supposed to send the Authorization header only once during the authentication. If the authentication is successful, the server sends you back a session cookie and your session is marked as authenticated (server-side).
You never send back the content of the header, and you don't have to send it each request.
1) The Authorization header is not automatically added. But the cookie will be automatically sent.
2) You should not send the credential and return them: for security purposes, you want to transport them the less you can.
3) You don't want to store the credential in the sessionStorage, I don't know if this is a secure place for a password (i doubt it), but here, the password is only encoded in B64, and it's reversable. So it's as well as cleartext (which is bad for a password).
Hopes this helps!
I am having a bit trouble understanding the following code
run.$inject = ['$http'];
function run($http) {
$http.defaults.xsrfHeaderName = 'X-CSRFToken';
$http.defaults.xsrfCookieName = 'csrftoken';
}
as I always thought that csrf is injected into html forms or ajax calls and not cookie, since csrf is a protection against any adversary trying to use your cookie for authentication.
Can someone give a detail explanation on how angular is handling csrf and how does it get the token from the backend?
I can't answer this better than the angular docs themselves do, so I'll just quote:
Cross Site Request Forgery (XSRF) Protection
XSRF is a technique by which an unauthorized site can gain your user's private data.
Angular provides a mechanism to counter XSRF. 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 could read the cookie, your server
can be assured that the XHR came from JavaScript running on your
domain. The header will not be set for cross-domain requests.
To take advantage of this, your server needs to set a token in a
JavaScript readable session cookie called XSRF-TOKEN on the first HTTP
GET request. On subsequent XHR requests the server can verify that the
cookie matches X-XSRF-TOKEN HTTP header, and therefore be sure that
only JavaScript running on your domain could have sent the request.
The token must be unique for each user and must be verifiable by the
server (to prevent the JavaScript from making up its own tokens). We
recommend that the token is a digest of your site's authentication
cookie with a salt for added security.
The name of the headers can be specified using the xsrfHeaderName and
xsrfCookieName properties of either $httpProvider.defaults at
config-time, $http.defaults at run-time, or the per-request config
object.
The $http.defaults.xsrfCookieName is just allowing you to specify what the name of the cookie is, otherwise it's going to look for the default XSRF-TOKEN.
On the server side implementation, I'd recommend using some node.js middleware to handle the setting of the initial cookie instead of rolling your own. Take a look at csurf in particular as it seems to be the most popular. You could also try senchalab's csrf middleware. Either of those ought to be enough to get you started.
I've just noticed it doesn't matter where I put my Authenticity Token when submitting a request via AJAX. I can either append it to the form as POST data, or put it into the Header.
Is there any difference? Especially regarding security?
Additionally:
I didn't encode the Token in Javascript. Am I exposed to something now?
Thanks in advance.
EDIT:
form.on("sending", function(file, xhr, formData) {
xhr.setRequestHeader('X-CSRF-Token', AUTH_TOKEN);
// formData.append('authenticity_token', AUTH_TOKEN);
});
This is my Javascript adding the token to the Header or (commented out) to the POST data. AUTH_TOKEN is the raw key. I did not encode it in any way.
Part one
There is totally no difference if you pass authenticity token via GET params, POST data or request headers (POST/GET params are virtually the same in Rails).
Let's look at the code (not the best code I've ever seen but...)
def verified_request?
!protect_against_forgery? || request.get? || request.head? ||
form_authenticity_token == params[request_forgery_protection_token] ||
form_authenticity_token == request.headers['X-CSRF-Token']
end
Request if valid if (any of following)
protect_against_forgery? is false
request is GET
request is HEAD
token in params equals one stored in session
token in headers equals one stored in session
I should add that token is generated for every request and stored in session for later inspection (if subsequent request is POST/PUT/PATCH/DELETE)
So as you see both ways of passing authenticity token are valid.
Part two
Is passing raw auth token in AJAX dangerous? No, as much as passing it in a form is totally not dangerous. To explain further I will quote an excellent answer in another SO question
Why this happens: Since the authenticity token is stored in the
session, the client can not know its value. This prevents people from
submitting forms to a rails app without viewing the form within that
app itself. Imagine that you are using service A, you logged into the
service and everything is ok. Now imagine that you went to use service
B, and you saw a picture you like, and pressed on the picture to view
a larger size of it. Now, if some evil code was there at service B, it
might send a request to service A (which you are logged into), and ask
to delete your account, by sending a request to
http://serviceA.com/close_account. This is what is known as CSRF
(Cross Site Request Forgery).
original answer: https://stackoverflow.com/a/1571900/2422778
I still consider this question laziness/lack of patience on your side as all I wrote is very well explained both in Rails Guides and on Stack Overflow. Hope next time you will be more persistent in looking for answers before posting here.
Anyway I am glad I could help.
You can see the difference when you use some tool like https://www.owasp.org/index.php/Category:OWASP_WebScarab_Project or http://www.charlesproxy.com/
That are proxies, which you can turn on locally to fiddle with your HTTP requests and responses.
Very useful for web development.
Good luck.