janko / rodauth-rails

Rails integration for Rodauth authentication framework
https://github.com/jeremyevans/rodauth
MIT License
584 stars 40 forks source link

Security issue with a "default config" #9

Closed nicolas-besnard closed 4 years ago

nicolas-besnard commented 4 years ago

After uncommenting this line (after_load_memory { two_factor_update_session("totp") if two_factor_authentication_setup? }), I realised that I can access protected resource via ajax even though I never entered the TOTP code. Trying to manually go to a protected page (by changing the URL) will redirect me to /otp-auth

After looking at the code, two_factor_authentication_setup? will return true if TOTP is configured, thus we will update the authenticated_by content even though we never really performed it.

To reproduce it, you just need to add a lit

I might have misconfigured something though

My rodauth_app.rb:

class RodauthApp < Rodauth::Rails::App
  configure(json: true) do
    # List of authentication features that are loaded.
    enable :create_account, :verify_account, :verify_account_grace_period,
      :login, :remember, :logout,
      :reset_password, :change_password, :change_password_notify,
      :change_login, :verify_login_change,
      :close_account, :two_factor_base, :otp, :recovery_codes

    after_load_memory { two_factor_update_session("totp") if two_factor_authentication_setup? }
    route do |r|
      rodauth.load_memory # autologin remembered users

      r.rodauth # route rodauth requests

      if rodauth.logged_in?
        Rails.logger.info '  -- rodauth.logged_in?'
        Rails.logger.info rodauth.logged_in?.inspect

        Rails.logger.info '  -- rodauth.two_factor_authentication_setup?'
        Rails.logger.info rodauth.two_factor_authentication_setup?.inspect

        Rails.logger.info '  -- rodauth.two_factor_authenticated?'
        Rails.logger.info (rodauth.two_factor_authenticated?).inspect

        Rails.logger.info '  -- (rodauth.logged_in? && rodauth.two_factor_authentication_setup? && !rodauth.two_factor_authenticated?)'
        Rails.logger.info (rodauth.logged_in? && rodauth.two_factor_authentication_setup? && !rodauth.two_factor_authenticated?).inspect

        Rails.logger.info '  -- rodauth.authenticated_by'
        Rails.logger.info rodauth.authenticated_by.inspect
      end

      if rodauth.logged_in? && rodauth.two_factor_authentication_setup? && !rodauth.two_factor_authenticated?
        rodauth.require_two_factor_authenticated
      end
    end
  end
end
janko commented 4 years ago

Thank you for the report. I'm not sure exactly how AJAX exactly bypasses this, but I think I see the issue, which is that the remember cookie is created when the user has logged in via password, but not yet two-factor authenticated. This means, even if we put AJAX aside, we still have the following vulnerability:

  1. an attacker logs in via email/password
  2. they're redirected to 2FA page, which they cannot complete
  3. they close the browser and open it again
  4. the session is remembered from the cookie
  5. the after_load_memory hook makes the session two-factor authenticated
  6. the attacker has gained access without having to complete 2FA

The AJAX probably triggers a similar behaviour. We should definitely remove this from default RodauthApp configuration, and I want to remove the auto-remembering as well.

I would like to add a guide at some point showing people how they can auto-authenticate 2FA when remembering (like GitHub does). I believe in this case we just need to call #remember_login only after 2FA (instead of after login):

after_two_factor_authentication { remember_login }

Do you see any issues with that?