Setup DB and run tests

rake

Run tests inside rails support app:

cd test/dummy
bin/rails test:system

# Run all developer integration tests
bin/rails test

# Run one developer integration test
bin/rails test --name test_booth_test_userland_otps_manage

Debugging webauthn: log/crmux.log


Generate documentation

gem install yardoc
yard server --reload

Public API

# As long as you only call methods in these modules,
# you are using the supported API of Booth.
Booth.some_method
Booth::Userland.some_method
Booth::Adminland.some_method

When testing you may use this

require 'booth/test'
# If you call methods in this module, you're using the official API.
Booth::Test.some_method

Brainstorming data models

User (email) (you own that)

Things that can initiate a session:

(A) Credential (username/password) (B) Registration (name, email) (C) Incognito User

Passport (session, belongs to A, B or C)

username

either PASS or WEBAUTH

Pass -> unknown username or password webauth -> fake challenge

questions

PRO USERNAME

  • enumeration-safe at login
  • can register as guest without immediate email access

PRO EMAIL

  • prevent enumeration

  • usernames or email addresses?

  • enumeration?

usernames: enumeration not such a problem email addresses: can recover, but inconvenient login VS enumeration, convenient registration anyway password guessing on admin login: of course people know our personal employee email addresses!

User Enumeration:


Assumptions

There may be multiple TABLES of users (e.g. customers and employees)

You can either use integers or UUIDs as primary keys

OTP is optional

Employees can incognito login as users and other employees

Incentives to use 2FA

  • password-only has global blocking (hacker may lock you out)
  • OTP has exponential blocking on IP-range and time (colleague may lock you out)
  • Webauth has no blocking (nobody can lock you out)

Possible improvements

  • Add an ephemeral credential ID to the password reset token. So when somebody tries to brute-force tokens, we can block per user, i.e. protecting all other users. But the tokens only live 2 hours, not enough time to brute force anything. I'm opening up for a DOS where the hacker locks out the user's password reset attempts Unless I make the credential ID ephemeral. But then it is just as likely that the hacker guesses the ephemeral ID as well, and then the token. And it adds complexity. A single token is really not complex. See https://security.stackexchange.com/a/99990 and https://github.com/jeremyevans/rodauth#label-Tokens

  • I could one-way hash the reset token and send the base to the user That way, if the database is leaked, no token in the DB can be used to reset anything (it's just digests). But then there is the change of digest collision if the digest doesn't map 1-1 to the original key length. Maybe it's better just to rely on the 2 hours validity of each password reset string. I think it's more likely somebody get's access to the original reset link in transmit (email provider, IT administrator, etc.) If you find a dump of our production database, it's likely older than 2 hours. And if you have direct access to our database, then why would you even try to reset a user account? You already have all user data.

Use Cases

Given an email address, send an invitiation link Given an email address

remote login assumptions:

a) non-trivial activity required on part of the remote session owner (i.e. don't just click to log in the new browser, but enter a code challenge-response or something) b) no guessing of the challenge by the new browser alone (i.e. don't just ask for a 9-digit response-number and that would immediately log you in)

Log out all sessions after changing password?

Let a new user choose password and OTP

Let a new user choose password and FIDO2

Change the onboarding email address

-> no, because we cannot check for duplicates during onboarding

Check status of user onboarding

Prevent hackers from guessing the password or the OTP via login brute-force

Login with username/password, then see safety phrase, need to enter OTP

Login with username/password, then see safety phrase, logged in

Maintain a server-side session

Logout this session

Logout all other sessions

Change your own login username

Change your own password (and logout all other sessions)

Change your own OTP

Add another OTP device (i.e. show OTP) needs: session

Can have same username in different scopes

Recover your own forgotten password

Admin allows you to choose new password

Admin allows you to choose new password and OTP

Admin allows you to choose new OTP

Assign another scope to a user

Usage


Let a new user choose password, OTP and safety phrase

Create new credential

If you want one of your users to login, you need to attach a credential record to them. The user will not be able to login at that time, because the password is random and unguessable.

You need to specify a (warden) scope where this credential is valid. E.g. :user, :admin, :customer

This method will create orphan credentials if called needlessly. This is because you fully own the reference to the credential in your business domain. It is your job to clean up orphan credentials (TODO: how?)

INPUT: NONE
OUTPUT: UUID of the created credential

id = Booth::Credentials.create scope: :admin
  #=> 'cbc140eb-1f14-4582-a33f-5b0067e10629'

MyUser.create credential_id: id

Generate Onboarding


Initiate onboarding

Security Documentation

Strategies for blocking further attempts after wrong password (or OTP)

Global block by number of attempts

TL;DR: Preferred for password-only credentials.

PRO

  • The only way to prevent password iteration given a known username.
  • It is simple and user-friendly to hide the password input-field if the limit has been reached.

CON

a) User can be locked out if hacker knows username. b) Hacker can know when block was lifted by customer support. c) Hacker can know at which point the user logged in (by observing change in the limit).

Mitigations

a) It is better for the (password-only) user to be locked out (and contact customer service) than to be exposed. b) Lifting the block is to be combined with changing the username. c) None. This is the major drawback of this strategy. But we already reveal whether a user is logged in or not. Also, when only WebAuthn is allowed, there is no limit and thus a particular user group (e.g. employee login) can be protected from leaking information by enforcing WebAuthn.

Comments

The pros and cons also hold true for a global exponential back-off or blocking per time interval. So there is no real advantage of those two variants over a global block by number of attempts.

IP-based exponential back-off block

TL;DR: Preferred for password-with-OTP-credentials.

PRO

  • User cannot be locked out from outside LAN if hacker knows username.
  • Hacker slowed down because more IPs are needed for password iteration.
  • User can be unblocked without unblocking the hacker.

CON

  • User can be locked out by a hacker in LAN.
  • Hacker in LAN can know when block was lifted.
  • Hacker in LAN can know when user logged in (by observing change in the limit).
  • Not really a hindrance to password iteration because many different IPs are available.
  • Effectively allows for password iteration of known usernames as long as IPs are available.

Mitigations

  • Do not limit passwordless logins (hardware is secure), limit OTP exponentially on /24-IP (to hinder slow-iteration) and only-password globally (to prevent iteration altogether).
  • Report when too many failed attempts were detected, regardless of IP.
  • Urge users to move to passwordless login (or at least 2FA).

  • On successful login unblock only exact IP

  • If exact user IP is known, lift the block of only an exact IP

  • If user IP is not known, change the username and then lift the block on all attempts

No block at all

TL;DR: Preferred for WebAuthn-credentials.

Reporting should still be in place, though, just as with password-and-OTP-blocks.

Things we intentionally reveal about the user with the username alice:

Did the user alice set an authentication mode yet (via onboarding?

Is the username Alice registered?

What is the user benefit?

How could an attacker abuse this?

Mitigations

Comments

We allow passwordless authentication via WebAuthn. That is somewhat problematic to implement when at the same time trying to avoid user enumeration.

Does the user Alice log in passwordlessly via WebAuthn?

What is the user benefit?

Most users won't even know what a WebAuthn device is. It would be confusing to let the user at login choose between entering a password or a hardware token. After a few months not using our service, the user might have forgotten whether a password or a hardware token was used during registration.

How could an attacker abuse this?

Knowing that Alice uses only a hardware key to authenticate, an attacker might be encouraged to steal the hardware key.

Mitigations

The risk of Alice physically loosing the hardware key is comparable to the risk of an attacker physically stealing the hardware key. We accept that risk. The possession of a hardware key is still considered safer than passwords.

Comments

An attacker knowing that Alice does not login passwordlessly is unproblematic. Because the password might still be combined with with 2FA.

We do not rely on Resident Keys to protect the user's privacy. But we always demand user verification for passwordless logins.

Is the user Alice logged in right now?

What is the user benefit?

Users experience frustration when they take up one of their many devices (phone, laptop, tablet) and they would like to use our services, but they are not logged in. The user is presented with a login prompt. Possibly in a hurry or without access to the login credentials (forgot password, no computer at hand, no password manager at hand).

In order to make it easier for the user to log in without any credentials in such a situation, we detect whether the user is already logged in on another, existing device. We then present instructions that instruct the user on how to authenticate the new device via the existing session on the existing device.

How could an attacker abuse this?

Once an attacker knows that Alice is a registered username, the attacker can naturally assume that Alice is logged in right now on some device. Yet by providing certainty about the user's login state, we provide the attacker with a time window of when it would be effective to launch an attack.

Suppose the attacker notices that Alice just logged in (for the first time) or that Alice just logged out (of the last active device). The attacker could then contact Alice, impersonate our employees, and gain trust by providing information about the session state of Alice's devices.

Mitigations

When a user attempts to log in on a new device we could simply always show the instructions of how to log in using another device (regardless of whether another device actually exists or not).

However, we would like to give the user certainty that there is no other way to log in right now, except by entering the credentials, when we ask for the credentials rather than showing instructions.

As for the impersonation, we are willing to take that risk. At the least, the attacker would have to know how to contact Alice. That is only possible for self-explanatory usernames (such as email addresses or publicly known unique names).

Comments

One could also signal on the logged in device that someone would like to log in right now (like Apple does it among their devices). But that's not feasible for a website. Nor do we want to open an attack vector where the user is alerted when an attacker merely tries to login, because some users will simply press OK.