ZuloOne
English
Sign in
Menu

🛡️ Sign-in protection

Updated

ZuloOne counts failed sign-ins and slows down anyone who keeps guessing passwords. Optionally, it adds a Cloudflare Turnstile check on the sign-in page.

← Business processes

User path: Open a stand.

How the process works#

Every wrong password is counted three ways for 15 minutes:

  • by account name — from any address;
  • by client address — for any account names;
  • by the pair of address and account name.

The counters decide two things:

  • Captcha. After 3 wrong passwords for one account, or 10 from one address, the sign-in page shows a Cloudflare Turnstile widget under the password field. Sign-in works again once the widget has passed the check. This needs Turnstile keys. Without them there is no captcha, and only the lockout below applies.
  • Lockout. The fifth wrong password from one address for one account stops sign-in for that pair for 30 seconds. Every further failure doubles the pause, up to 15 minutes. While the pause lasts, the server does not check the password at all, so guessing cannot continue. The Sign in button shows a countdown.

An account name alone never locks anything. Otherwise anyone could keep a colleague out by typing their name wrong on purpose. The owner signing in from their own address is not affected by someone else's lockout.

One public address that fails 50 times in 15 minutes, across any names, is locked as a whole for 15 minutes. A private address, such as a proxy or an office network, is never locked as a whole, because everyone behind it would be locked out. It still gets the captcha.

A correct password clears the account and pair counters. The address counter stays. One valid sign-in from an attacking address does not erase what that address did to other accounts.

The self-service password change counts wrong old passwords the same way and has the same lockout. It does not show a captcha.

Settings#

All settings are server configuration: appsettings.json or environment variables on the tenant container. There is no settings card in the application. A change needs a container restart.

Setting Values Default Effect
Turnstile:SiteKey / Turnstile__SiteKey Cloudflare site key empty Public key for the widget. Empty = no captcha.
Turnstile:Secret / Turnstile__Secret Cloudflare secret empty Checks a solved widget on the server. Empty = no captcha.
Turnstile:ExpectedHostname host name empty The token must be solved on this host. The control panel sets it to the tenant host.
Security:Login:CaptchaMode Off, AfterFailures, Always AfterFailures Off: never ask. AfterFailures: ask after the thresholds below. Always: every sign-in passes the check (the widget stays invisible unless Cloudflare wants a click).
Security:Login:CaptchaAfterAccountFailures number, 0 = off 3 Wrong passwords for one account before the captcha.
Security:Login:CaptchaAfterAddressFailures number, 0 = off 10 Wrong passwords from one address before the captcha.
Security:Login:LockoutAfterFailures number, 0 = off 5 Wrong passwords of one address for one account before the pause.
Security:Login:LockoutBaseSeconds seconds 30 First pause. It doubles on every further failure.
Security:Login:LockoutMaxSeconds seconds 900 Longest pause.
Security:Login:AddressLockoutAfterFailures number, 0 = off 50 Failures from one public address before the whole address pauses.
Security:Login:AddressLockoutSeconds seconds 900 Length of an address pause.
Security:Login:WindowMinutes minutes 15 How long a failure is remembered.

In environment variables, : becomes __, for example Security__Login__CaptchaMode=Always.

Provisioned tenants get the Turnstile keys and the client-address header from the control panel. The operator steps are in the deployment repository, prod/INSTALL.md, section "Tenant sign-in captcha".

The client address must be real. Behind Cloudflare the tenant needs ZuloOne:ForwardedForHeader=CF-Connecting-IP. Without it every caller appears as the proxy's address. The address counters then add up everyone's mistakes, and after 10 typos anywhere every user sees the captcha.

Example with numbers#

Defaults, Turnstile keys set. Anna's account is anna. An attacker works from 203.0.113.7.

# From Account Password Answer
1–2 203.0.113.7 anna wrong Invalid username or password
3 203.0.113.7 anna wrong Invalid username or password; the widget appears
4 203.0.113.7 anna wrong, widget passed Invalid username or password
5 203.0.113.7 anna wrong, widget passed Pause of 30 s: the button shows Try again in 0:30
6 203.0.113.7 anna any, after 30 s If wrong: pause of 60 s
— Anna's office anna correct, widget passed Signed in. The pause applies only to 203.0.113.7 + anna

The same attacker then tries 50 different names within 15 minutes. 203.0.113.7 is a public address, so it is locked for 15 minutes for every account.

Boundaries and edge cases#

  • Counters live in the memory of the tenant process. A restart forgets them. This is deliberate: one tenant is one process, and a restart costs an attacker seconds, not the account.
  • If Cloudflare cannot be reached, the check fails closed. The person sees "The check did not pass" and cannot sign in until the account's failures expire.
  • A failed Turnstile check counts as a failed attempt.
  • Clients that cannot draw the widget, such as the mobile app and scripts, keep working until the captcha threshold. After it they are refused with the code captcha_required (HTTP 428) until the failures expire, or until the person signs in once in a browser.
  • The administrator lock (Locked on the user card) is separate. It refuses a correct password and never expires by itself.
  • Directory sign-in through login.getzulo.com has its own Turnstile and is not counted here.

What's not there yet#

  • No list of locked accounts or addresses on screen, and no button to lift a pause early. Restarting the tenant clears all counters.
  • No e-mail or notification to the account owner after repeated failures.
  • No settings card: thresholds change only in configuration.
  • No two-factor confirmation for tenant users; the Two-factor fields on the user card are not enforced at sign-in.

Where next#

Was this page helpful?