CSRF token session_store with ember-simple-auth alongside Devise - ruby-on-rails-4

Is it possible to implement Rails csrf through cookie_store at the same while using ember-simple-auth Devise?
Guides like this one always deactivate Rails.application.config.session_store which from my understanding does not allow Rails to keep track of csrf tokens which causes Rails to lose track of sessionsg. After attempting many solutions including:
require jquery_ujs on Rails manifesto.
Rails.application.config.session_store :disabled.
https://github.com/abuiles/rails-csrf.
Changing Ember.js Adapter to append CSRF.
The end result is still pretty much the same:
Can't verify CSRF token authenticity followed by Completed 422 Unprocessable Entity if protect_from_forgery is set with :exception instead of :null_session.
Example Transaction:
Partial Request HEADER:
X-CSRF-Token:1vGIZ6MFV4kdJ0yYGFiDq54DV2RjEIaq57O05PSdNdLaqsXMzEGdQIOeSyAWG1bZ+dg7oI6I2xXaBABSOWQbrQ==
Responder HEADER
HTTP/1.1 422 Unprocessable Entity
Content-Type: text/plain; charset=utf-8
X-Request-Id: 71e94632-ad98-4b3f-97fb-e274a2ec1c7e
X-Runtime: 0.050747
Content-Length: 74162
The response also attaches the following:
Session dump
_csrf_token: "jFjdzKn/kodNnJM0DXLutMSsemidQxj7U/hrGmsD3DE="
The rails-csrf response from my csrf branch (branch has been deleted).
beforeModel() {
return this.csrf.fetchToken();
},
Partial dump of the return statement:
_result: Object
param: "authenticity_token"
token: "1vGIZ6MFV4kdJ0yYGFiDq54DV2RjEIaq57O05PSdNdLaqsXMzEGdQIOeSyAWG1bZ+dg7oI6I2xXaBABSOWQbrQ=="
From my understanding, all of these attempted solutions have the common root: session_store is disabled...

Update!
The answer below turned out to be wrong in nature after learning more about CSRF protection and Ember-Cli-Rails.
The idea here is to have two storages: A cookie-based storage only for the csrf token that is maintained by Rails, and a localStorage maintained by Ember-simple-auth where the user authenticity token, email and id are being taken care of while a custom session SessionAccount inherits those values and validates them against the server before setting the user that will be available for the entire Ember.js.
The validation by the SessionAccount occurs in order to detect any tampering with the localStorage. The validation occurs every time the SessionAccount queries the localStorage (e.g page reload) as it communicates with the server through a Token model (token, email and id.) The server responds with 200 or 404 through a TokenSerializer which only renders email or the validation error, thus not disclosing the frontend to see other authentication_tokens unless the user sign in through the login form which requires email and password.
From my understanding, the weak spots in this methodology are not susceptible enough to be so easily hackable unless:
Someone invades the server and get the database content. Although the passwords are salted, any person who has the database dump can change the localStorage token, email and id to the person they want to impersonate and the server validation will work. However, this can be minimized by a worker that changes the authentication token for non-logged in users every 24 hours (or any other timeframe.) The code example section currently does not have the worker since I still have not learned about them.
Someone know the password and email of the person they want to hack... Not much I can do about that one at the moment.
Someone intercept the data being passed around through the JSON API. A strong SSL implementation should do a good job.
If your sessionAccount has something in the lines of is_Admin, then the token could be sent alongside the POST request for admin only requests in order for further backend validation since you can never trust the frontend.
Something else? Those are the ones I am aware of.
Now onto the practical approach:
Set
Rails.application.config.session_store :csrf_cookie_store, key: '_xxxxx_id', httponly: false on the session_store.db.
Create the csrf_cookie_store under lib and require it on application.rb with require 'csrf_cookie_store'.
Set protect_from_forgery with: :exception on application.rb.
Create a TokenController to handle Validation.
TokenSerializer so that only the email is sent back.
Update your Devise Session controller to change the token upon login and to skip authenticity token validation on session destroy.
Check your routes for the tokens to be create only and to have the custom Devise session.
Create the Ember.js Token Model
Match your SessionAccount to what I created.
Update your Devise Authenticator to send a delete request to the server when the session is being invalidated.
Test it out.

Related

django rest framework - session auth vs token auth, csrf

I have DRF set with the default settings. My ajax clients works fine with the session authentication. I want another remote server to consume the same API as the javascript clients.
My login code is simple:
class Login(APIView):
def post(self, request, *args, **kwargs):
user = authenticate(username=username, password=password)
if user is None:
return Response(status=status.HTTP_401_UNAUTHORIZED)
login(request, user)
# ...
The issue is when I use a client from another host, like python requests, I get a CSRF error. According to DRF docs, I think I should use a token authentication instead.
Questions:
Why do I need token authentication? The sessionid cookie is already a token, why I can't use it both for ajax clients and software clients? Thus avoid another separate db table for the tokens.
Since I do want to use only session authentication, how to enforce CSRF only for ajax clients?
It's not really compulsory to use Token Authentication, just that Session Authentication is liable to CSRF attacks. You can try to prevent this using CORS mecahnisms and CSRF tokens but it is still not entirely safe.
To be honest, Token Authentication doesn't entirely work well with browsers either as the token can be easily retrieved using the browser's developer tools if you don't use a very complex and sophiscated mechanism for handling it. It's just simpler to use it for third-party apps.
Though CSRF attacks are only applicable to browsers(Ajax clients), you shouldn't try to exlude them because the method of checking if the request is from an ajax client request.is_ajax() depends on whether the client has set the X-Requested-With header. It may be possible for an attacker to remove this header. Again I would advise that you also add CORS verification which is the method used by browsers to safeguard against CSRF attacks in addition to Django's CSRF tokens. This is typically done using Django-cors-headers package
And why token authentication isn't subject to csrf attacks? It does not seem more secure to me than the session. As I see it, both of them use HTTP headers to pass a token (in token authentication is in the Authorization header, and session is a cookie which is also a header)
Tokens are sent using the Authorization header(you could also decide to use a custom header but this is the standard for interoperability) while session auth uses cookies which are automatically sent by the browser and this is why they're susceptible to CSRF attacks. For tokens, the client has to explicitly set the header so it has to know the token, while the attacker will not even have to know what is stored in the cookies as the browser just automatically sends whatever is in its cookie store for that site.
You shouldn't enable CSRF protection for ajax clients only – it doesn't make any sense. How can you differentiate between "ajax" client and "normal" client? If it will be done e.g. by some query param, then an attacker can just use this "normal" link to do bad things.
When you're using token-based authentication, an attacker cannot just use common URL to make your request be authenticated transparently. That's why only session-based authentication requires a valid CSRF token to be included into request.
So for security reasons there are 2 options:
either use session-based authentication, but then you need to send auth cookie and CSRF token with every request;
or use token-based authentication, which is simpler since you only need to provide auth token e.g. as a query param.
Can I use token authentication that gets the token from the standard django_session table? just use that as token?
In theory you can achieve that by writing some custom authentication middleware that will use token from query param and match it with session table, but that's generally bad idea.
First, there's no such a big overhead in using one more table, but without it you're making the system harder to read and maintain.
Second, it will make the system more fragile as well. Since sessions and tokens are 2 completely different entities, they can have e.g. different lifetime. Sessions can be flushed, their TTL can be shorter/longer than token TTL. For example, default django session TTL is 2 weeks. Do you want to complicate remote server logic to get new token every 2 weeks? Or imagine the situation when token is compromised. Do you want to force ajax client to log out as well?

Is it okay to disable CSRF for slack slash command api mad in Django?

Is it disabling CSRF functionality the best practice for Slack slash command server?
I want to call an API view function in Django by a Slack slash command, for example, /test.
When I call the URL for the view function with any browser (so it is a GET request), it works as expected.
However, when I run /test in Slack, I got 403_client_error in slack, and Forbidden (CSRF cookie not set) in the Django shell.
I believe this is because Slack sends a POST request, and Django requires CSRF token for any POST requests.
My question is whether I should disable CSRF checking for this view. Will there be a significant risk? Or is there any workaround?
Short Answer:
Yes, it's fine for an API endpoint.
Longer Answer:
Cross-Site Request Forgery (CSRF) Tokens are a means of preventing CSRF Attacks. These attacks basically work by placing some malicious code on another site that runs when the user visits that site. Upon loading the malicious site, some JavaScript code runs and submits a POST request to a site that the victim is already authenticated at. Since the user is already authenticated, the server for the other site—a bank, for example—thinks that the user is actually making this request—send the attacker $100, for example—and goes ahead and processes the request. Here is a really good description of CSRF Attacks and Tokens: https://stackoverflow.com/a/33829607/8068625
The way that CSRF Tokens prevent this is by putting a token somewhere on any page that contains a form and that token gets submitted as an additional field on the form when submitted. This way the page must actually be visited (which the attacker should not be able to accomplish) in order to submit the page.
So, as long as this page you are removing the CSRF protection from does not have a form on it that you would not like submitted by an attacker, it is fine to add #csrf_exempt to the view. It seems that this is the case since the endpoint exists solely as an API endpoint for a Slack Slash Command.
You might consider adding some other form of authentication for your API endpoints (note that CSRF is not for use in APIs) like, for example, JSON Web Tokens. But that is up to you.

CSRF token is incorrect after login in SPA, but correct after page refresh

We make react SPA with django-rest-framework on backend and use django-rest-auth for user authentication.
When user has logged in, we show him form for change profile data. When user submit this form, we take csrf token from cookie in login response, and put them in request X-CSRFToken header. Server responses that token is missing or incorrect.
If user refreshed the page, and repeated the same actions, csrf token is correct and profile data is updated.
How to solve this problem and why it occurs?
It looks like this is happening:
The login is successful
The cookie+token is created by the CSRF API and returned to the endpoint
The ready state executes some code not executed after the API response
The certificate is validated and the cookie+token is set in the database/app config/server-side cache during a GET request and/or by the ready state callback
Since there is no GET request until the refresh, the cookie+token is not centrally stored until then. Add a request to mimic what happens during the GET, then store it where it is currently being accessed for subsequent requests.
References
Issues with CSRF token and how to solve them | SAP Blogs
Why refresh CSRF token per form request? - Information Security Stack Exchange
CSRF Protection — Flask-WTF 0.14
CSRFGuard 3 Configuration - OWASP
Spring Security: Cross Site Request Forgery (CSRF)
Cross Site Request Forgery protection | Django documentation | Django
XSRF/CSRF Prevention in ASP.NET MVC and Web Pages | Microsoft Docs
Cross-Site Request Forgery is dead!
Still think you don't need HTTPS?

Should CSRF tokens be server-side validated?

First, I want to make sure I got the CSRF token workflow right.
The server sets a cookie on my machine, on the site's domain. The browser prevents access to this cookie from other domains. When a POST request is made, I send the CSRF token to the server that then compares it to my cookie. It they're not the same, a 403 Forbidden page is returned.
Now, if I manually change the value of the token in the cookie and send that new value in the POST request, should the server return a 403 or not? Does the server need to validate the token agains a value stored on the server or on the cookie?
I am using the default implementation of CSRF protection on Django 1.3 (https://docs.djangoproject.com/en/1.3/ref/contrib/csrf/) and it validates the token sent in the request against the token only.
How do you send the token?
Usually, the tokens should be some function (with a secret key - known only to the server; e.g., MAC) of the cookie! not the cookie.
Than the flow is as follows:
1. Client sends the server request with a cookie.
2. Server returns a web page with CSRF token(s) for different purposes (e.g., forms or just a simple get requests via the URL).
3. The client performs some action (via POST or GET) and sends request with the token (in the request body or in the URL) and with the cookie.
4. The server is stateless, but it can verify that the request was sent by the same client by calculating the function (with the secret key that the server knows) on the cookie (or on part of it), and comparing the output with the token.
In the case of CSRF, the cookie is automatically appended to the request by the browser, but the attacker (that probably even doesn't know the cookie) cannot add the corresponding tokens.
I believe you should do something like this.
Now, if I manually change the value of the token in the cookie and
send that new value in the POST request, should the server return a
403 or not? Does the server need to validate the token agains a value
stored on the server or on the cookie?
The server should be stateless (usually). You don't want to verify the token every request against some value in a database or something like that. It is better to verify against the cookie.
In that case, if you change the token, than it probably won't match the cookie, and you should send 403.
TL;DR: Yes, either you, or the framework you are using, needs to have server-side logic to validate a CSRF token. It cannot be a cookie, it has to be something that requires the user to be on your page, versus click on a link an attacker provides.
You've got the workflow pretty much correct. The first step is to generate a cryptographically random string that cannot be predicted by an attacker. Every programming language has its own construct to do this, but a 24 - 32 character string should be good to serve the purpose.
Before we get to the next step, let's make sure we know what threat we're dealing with - we don't want an attacker to make a request on behalf of the user, so there should be something that is accessible to the browser that requires the user to perform an action to send the token, BUT, if the user clicks on something the attacker has set up, the token should not be sent.
Given this, the one way this should NOT be done is using cookies. The browser will automatically send cookies every single time a request is made to the domain the cookie is set on, so this automatically defeats our defense.
That said, let's go to the next step, which is to set this token in a way that is verifiable by you on the server side, but not accessible to the attacker. There's multiple ways to do this:
1) A CSRF Header: This is done in many node.js/Express installations - the CSRF token is sent as a header, to be specific, a X-CSRF-Token header. After generating this token, the server stores this in the session store for that particular cookie. On the front end, the token is stored as a JavaScript variable, which means only requests generated on that particular page can have the header.. Whenever a request is made, both the session cookie (in the case of node.js, connect.sid) and the X-CSRF-Token is required for all POST/PUT/DELETE requests. If the wrong token is sent, the server sends a 401 Unauthorized, and regenerates the token, requesting login from the user.
<script type="text/javascript">
window.NODE_ENV = {};
window.NODE_ENV.csrf = "q8t4gLkMFSxFupWO7vqkXXqD";
window.NODE_ENV.isDevelopment = "true";
</script>
2) A Hidden Form Value: A lot of PHP installations use this as the CSRF defense mechanism. Depending on the configuration, either a session specific or a request specific (latter is overkill unless the application needs it) token is embedded in a hidden form field. This way, it is sent every time a form is submitted. The method of verification varies - it can be via verifying it against the database, or it can be a server-specific session store.
3) Double Submit Cookies: This is a mechanism suggested by OWASP, where in addition to sending the session cookies via the header, you also include it in the forms submitted. This way, once you verify that the session is valid, you can verify that the form contains the session variables also. If you use this mechanism, it is critical to make sure that you validate the user's session before validating CSRF; otherwise, it introduces flaws.
While building/testing this mechanism, it is important to note that while a lot of implementations limit it to POST/DELETE/PUT transactions, this is because it is automatically assumed that all sensitive transactions happen through this verbs. If your application performs sensitive transactions (such as activations) using GET, then you need this mechanism for GET/HEAD also.

Django CSRF cookie accessible by javascript?

On django website, https://docs.djangoproject.com/en/dev/ref/contrib/csrf/ it states:
The CSRF protection is based on the following things:
1. A CSRF cookie that is set to a random value (a session independent nonce, as it is called), which other sites will not have access to.
2. ...
Then, it also states the csrf token can be obtained from cookie by javascript:
var csrftoken = $.cookie('csrftoken');
Aren't these two statements conflicting? Say there is a Cross Origin attack, then the attacker can just obtain the CSRF token from cookie, and then make a POST request with the CSRF token in the header? Can someone explain this please?
UPDATE
I realize now that, only the javascript from the same origin is allowed to access the cookie. A follow-up question is:
If a POST request automatically adds the cookie as part of the request, and django's csrf cookie value is the same as csrf token, then a malicious cross source request will still have the correct CSRF token anyways? (in cookie)
I believe that this post answers your updated question:
Because of the same-origin policy, the attacker cannot access the cookie indeed. But the browser will add the cookie to the POST request anyway, as you mentioned. For this reason, one must post the CSRF token from the code as well (e.g. in a hidden field). In this case, the attacker must know the value of the CSRF token as stored in the victim's cookie at the time she creates the malicious form. Since she cannot access the cookie, then she cannot replicate the token in her malicious code, and the attack fails.
Now, one might imagine other ways of storing the token than in the cookie. The point is that the attacker must not be able to get it. And the server must have a way to verify it. You could imagine saving the token together with the session on the server-side, and storing the token in some "safe" way on the client side ("safe" meaning that the attacker cannot access it).
Here is a quote from OWASP:
In general, developers need only generate this token once for the current session. After initial generation of this token, the value is stored in the session and is utilized for each subsequent request until the session expires. When a request is issued by the end-user, the server-side component must verify the existence and validity of the token in the request as compared to the token found in the session. If the token was not found within the request or the value provided does not match the value within the session, then the request should be aborted, token should be reset and the event logged as a potential CSRF attack in progress.
In the end, the security needs two things:
The CSRF token must be sent from the code, which means that the malicious code must know it.
The CSRF token must be stored in some "safe" place for comparison (the cookie is convenient for this).
I am not a specialist, but this is my understanding of the problem. Hope it helps.
From the name CSRF (Cross Site Request Forgery), you can already guess the attacker must perform the request from "cross site" (other site).
"The key to understanding CSRF attacks is to recognize that websites typically don't verify that a request came from an authorized user. Instead they verify only that the request came from the browser of an authorized user." - quoted here
So for sites that don't prevent CSRF attacks, the attacker can send the malicious request from anywhere: browsers, emails, terminal... Since the website doesn't check the origin of the request, it believes that the authorized user made the request.
In this case, in every Django form, you have a hidden input called "CSRF token". This value is randomly and uniquely generated at the time the form rendered, and will be compared after the request has been made. So the request can only be sent from the authorized user's browser. There is no way (which I know of) an attacker can get this token and perform the malicious request that can be accepted by Django backend.
Clear enough?