Post CSRF check, devise still runs full function - ruby-on-rails

In Rails, using devise, if a CSRF Check fails then the user's current session is cleared, i.e., logs the user out, because the server assumes it's an attack (which is the correct/desired behavior).
But the request, is completed, hence the user record is still created. Hacker can then log in correctly.
How can I stop the method from continuing once devise realises auth_token is incorrect?

Devise doesn't do any checking of the auth token - it's action controller which does this (although it does call handle_unverified_request on your controller so that you can customise behaviour). In rails 4 and higher you can also specify what happens by default when the auth token is invalid:
protect_from_forgery with: :exception
causes an exception to be raised, which would stop the request being processed.
However I am not sure what this buys you though - CSRF is so that an attacker cannot abuse the fact that the user is already logged into your application, but if the attacker has a valid set of credentials then they don't need to do CSRF in the first place.

Related

Why am I receiving 401 Unauthorized errors with my Doorkeeper configuration?

I have a Rails 6.1 app using devise 4.7.1, doorkeeper 5.5.1, and devise-doorkeeper 1.2.0.
I'm trying to run through a (PKCE) OAuth flow, but the final step -- a POST request to /oauth/token -- returns a 401 Unauthorized error with the JSON content {"error": "You need to sign in or sign up before continuing."}.
I'm confused about this, since the /oauth/token endpoint should be accessible to unauthenticated users as far as I understand. What's also weird (but perhaps a red herring) is that if I attempt to run the same POST request with curl, but remove the User-Agent header, it succeeds.
My current suspect is this block of code in initializers/doorkeeper.rb:
resource_owner_authenticator do
current_user || warden.authenticate!(scope: :user)
end
This comes from the Doorkeeper docs. By stepping through the code, I can see that it's the call to warden.authenticate! that returns a 401 error. Doorkeeper's TokensController#create is never called.
Is there any important step I'm missing that allows unauthenticated access to this TokensController#create endpoint?
This problem was caused by our use of the Ahoy analytics library.
By default, this library tracks all page visits in your Rails app. It tries to get the current user using current_user || current_resource_owner. Because current_user was still nil when POSTing to /oauth/token, getting current_resource_owner ended up calling our Doorkeeper resource_owner_authenticator, which returned the 401 error. The source code for this is here.
This also explains why things worked as expected when unsetting the User-Agent header: with no user agent (or the user agent of e.g. curl), Ahoy treats the request as coming from a bot, and doesn't attempt to track it (source code here).
Our solution to this is to tell Ahoy to stop tracking all page views automatically by setting Ahoy.api_only = true in its configuration.

How to debug warden/devise because it's ignoring a cookie

I used SAML strategy for devise (https://github.com/apokalipto/devise_saml_authenticatable).
I did some modifications to it and I see that if a user isn't authenticated it goes and authenticate a user and I log that sucess!() called. As result, browser is getting a session cookie.
The browser coming back to my website with this session cookie. However, warden requires to authenticate again (leading to never ending authentication loop) instead of just letting this user through.
My question is: How can I debug it to see why warden calls authentication (vs just letting it through).
I added before_action to ApplicationController and did puts request.env. I see that session cookie is coming. However, I am not sure what should be the next step to check what's going on.

Devise Warden Authentication Fails First Time, Succeeds After

I am using Devise to authenticate users for my rails app using database authentication (for username and password) and token authentication for an API that I built with Grape. Devise is generating an authentication token as expected. However, it seems that authentication always fails after the first request and works subsequent times. I am calling authenticate! before my API calls in Grape, which is defined as follows:
def authenticate!
error!({"error" => "Unauth 401"}, 401) unless env['warden'].authenticate
end
This is very odd behaviour. If I try to login with the browser first, then via a curl call to the API, it works. It seems that the first request will just always fail after a server restart.
Is that the expected behaviour? If so, why is that and how do I avoid it? Do authentication key logins always need a regular login via the browser first?
P.S: I did read Devise authentication fails on first attempt, succeeds afterwards, but it does not seem to answer the question.

Occasional 422 error: client not sending session cookie?

A few users are getting a 422 ActionController::InvalidAuthenticityToken error when POSTing a form.
It happens to a minority of users some of the time. If they try their request again later, it often works.
The authenticity token is getting sent along in every case. I'm assuming the client isn't sending the session cookie along with the POST (that would explain why the server can't verify the token). Why would this be?
Finally, the form is submitted via javascript ($('#new_user')[0].submit()), would that somehow prevent the session cookie from being sent?
Disabling the verify_authenticity_token before_filter is unfortunately not an option.
We have run into this scenario with one of our apps. We store our sessions in memcached and if the session is evicted from the cache or the session expires any subsequent post/put/delete raised a 422. We got round this by implementing a before filter 'requires_login?' that checked the session and logout the user out if the session had expired. We then moved the method protect_from_forgery in the application controller to run after requires_login?
E.G
before_filter :requires_login?
protect_from_forgery
Hope this makes sense

Rails app session not recognized for iOS NSURLRequests

I have a simple rails app with a single controller. I have a "before filter" for some of the methods of my controller, where I check if the user is logged in by looking at the session object:
#user = User.where(:id => session[:user_id]) if session[:user_id]
In a "login" method of my controller, I do:
session[:user_id] = user.id
Pretty usual stuff. If I access my app from a web browser (Chrome), everything works fine. However, when I use NSURLRequest from my iOS app to access my rails app, the server always creates a new session for each request. It never seems to be able to identify the existing session, even though the request is sending the cookie with the proper session ID in it. In fact, if I look at the "cookies" object in my rails app, I can see it contains the session ID. However, the session object is always empty! Not sure why the server is not able to retrieve the session. I'm using Passenger Phusion. Any suggestions?
If you are POSTing to your login page, and the post does not include a CSRF token matching the token in the session, then Rails will fail the request and invalidate/reset the session as a security precaution.
To fix it, simply read the CSRF token out of the session and include it in your request, or turn off CSRF token checking, you can place skip_before_filter :verify_authenticity_token in your controller to skip the CSRF protection checks. Note that the latter approach does open potential security holes, so including the tokens and checking them is recommended if it is at all viable.

Resources