AWRA OpsHub Search

The Setting That Only Tightens

A workspace can make its password rules stricter and cannot make them weaker: the minimum length is a floor rather than a value, and complexity has no off switch anywhere in the code. Beside it sits a lockout keyed on the address typed rather than the account found — which is the only way to stop the lockout becoming a way to discover who has an account.

Operations Metrics AWRA OpsHub Team 14 min read

Almost every security setting in almost every product is a dial that turns both ways, which means the weakest configuration any customer chooses is the weakest configuration the product has.

This is the ordinary shape of a configurable policy, and it is worth noticing how odd it is. A vendor decides that twelve characters is the right minimum, ships it as a default, and provides a field to change it. Somebody sets it to six because a colleague complained. The product now enforces six, and the vendor's judgement about twelve is a memory.

A different arrangement is possible, and it is barely more work: make the setting a floor rather than a value.

Rules that only move one way

The password rule here takes the configured minimum length and the baseline minimum length, and uses whichever is larger. A workspace that sets sixteen gets sixteen. A workspace that sets six gets twelve. There is no configuration that produces a weaker rule than the one the product ships with.

Complexity works the same way and is stricter still: mixed case, numbers and symbols are applied unconditionally. Not "applied unless a flag turns them off" — there is no branch. The flag that exists can only ever add complexity back if a future baseline were to drop it.

What a workspace can and cannot change

Setting Can tighten Can loosen
Minimum password length Yes No
Mixed case, numbers, symbols No No
How many old passwords are remembered Partly — configurable by you Yes
Password expiry Yes Yes
Failed-attempt lockout threshold Yes Yes
How long a lockout lasts Yes Yes

Built and maintained Configurable by you, not maintained by us Not built

Password history shows partial on tightening because it is capped: however high the policy is set, no more than twenty-four previous passwords are ever compared against. The cap is a performance decision, and the reason is in the next section. The bottom three rows are ordinary two-way settings, and saying so is more useful than implying the whole policy object works one way.

Why this matters more than it sounds

A floor changes who is accountable for a weak configuration. With a two-way dial, a vendor can ship a good default, watch a customer weaken it, and describe the resulting breach as a configuration choice. With a floor, the vendor has taken a position and lives with the support burden of it. The cost is real — somebody will want six characters and will be told no — and it is the correct cost to pay.

The cap on password history, and what it is protecting

Password history exists to stop one specific move: being forced to change a password and immediately changing it back. To enforce it, a new password has to be compared against the stored hashes of old ones.

Comparison is the expensive part. A password hash is deliberately slow to compute — that is the entire point of the algorithm — and a candidate must be run against each retained entry separately, because hashes cannot be compared to each other, only tested against a candidate. Twenty-four retained passwords is twenty-four slow computations, on a form submit, while somebody waits.

So the count is capped regardless of policy. Somebody setting it to two hundred gets twenty-four, and the alternative would be a password-change form that visibly hangs.

One detail that is easy to get wrong here

What gets stored in history is the hash taken off the account record, not a fresh hash of the plaintext. Those sound identical and are not: re-hashing the plaintext produces a different string with a different salt, and — more importantly — a different hashing configuration if the settings have moved since. Taking the stored value means the history holds exactly what was in use, which is the only thing a future comparison can be meaningful against.

Expiry, and the account that never set a date

Password expiry is a straightforward setting: after so many days, a password must be changed. The interesting case is an account where the date the password was last set was never recorded.

There are two ways to treat that, and they are opposites. Treat the missing date as very old, and every such account is forced through a reset immediately. Treat it as not expired, and those accounts sit outside the policy until they next change a password.

The choice made here is the second, on the grounds that a missing date is a gap in the data rather than evidence about the password. Nothing about an unpopulated column says the password is old. Forcing a reset on that basis locks out accounts that did nothing wrong, on the strength of an inference the data does not support.

It is a genuine trade and worth stating as one. The security-maximalist reading is that an unknown age should be treated as the worst case. The counter-argument, and the one taken, is that a control which locks out innocent people on the strength of a missing value gets switched off within a week, and a control that is switched off protects nobody.

The lockout that must not become a directory

Now the part of this that most implementations get wrong in a way that is invisible from the outside.

An account lockout counts failed sign-ins and blocks further attempts once there have been too many. The natural way to build it is to count failures against the account — find the user, increment their counter, lock them when it reaches the threshold.

Which produces a behaviour nobody intended. Try six wrong passwords against an address that has an account: locked. Try six against an address that does not: nothing happens, because there is no account to increment a counter on.

The lockout itself becomes an account-enumeration oracle, with real addresses locking and invented ones never doing so.

The difference in response tells an attacker which addresses are real. That is worth having: a list of confirmed accounts is the input to every other attack — credential stuffing, phishing, password spraying — and it has been handed over by the control that was supposed to protect them.

The fix is to count against the address typed, not the account found. An invented address accumulates failures and locks exactly like a real one, so both behave identically and neither confirms anything.

And the address is not stored in the clear

The counter is keyed on a hash of the address, lowercased and trimmed first. Two consequences, both wanted. The store holds no readable list of addresses people have tried to sign in as — which would otherwise be a rather good directory of both your users and whoever an attacker is guessing at. And normalising before hashing means the same address typed with different capitalisation or a trailing space lands on one counter rather than splitting into several, which would quietly multiply the real threshold.

Two clocks, and why both exist

A lockout is not a rate limit and the two are often confused, including by people building them.

A route rate limit

  • Counts requests from one client to one endpoint.
  • Stops a burst: a thousand attempts a minute from one place.
  • Blind to a slow attack, and blind to a distributed one.
  • Five attempts an hour each from two hundred addresses passes it completely.
  • Still wanted. It is the cheap, broad defence.

An account lockout

  • Counts failures against one address from anywhere at all.
  • Stops a slow grind: a handful of guesses a day against one account.
  • Indifferent to where the attempts came from, which is the point.
  • Catches exactly the pattern the rate limit cannot see.
  • Needed as well. Neither substitutes for the other.

The attempt window is set equal to the lockout duration, which is a smaller decision with a real consequence. Failures spread further apart than a lockout would have lasted are not the pattern the control is for, and counting them indefinitely would eventually lock out a long-standing user with an ordinary history of typos — a control that becomes more hostile the longer somebody has been a customer.

And the counters live in the cache rather than a table. This is a deliberate trade with a visible downside: clearing the cache clears active lockouts. Against it, a table would mean a database write on every mistyped password, which is a write endpoint an attacker can drive for free. In this deployment the cache sits on the shared database, so the counters still hold across servers — a workspace is not one lockout per machine.

Five questions for any sign-in you are assessing

Can we configure the password minimum below your default?

A good answer sounds like

No, the default is a floor.

What ours actually is

No. The rule takes the larger of the configured value and the baseline of twelve, and complexity has no off switch in the code at all.

Does a lockout behave the same for an address with no account?

A good answer sounds like

Yes, identically.

What ours actually is

Yes. Failures are counted against the address typed rather than the account found, so an invented address locks exactly as a real one does. This is the question that finds the enumeration oracle, and it is almost never asked.

Do you have a rate limit as well as a lockout?

A good answer sounds like

Both, and here is what each catches.

What ours actually is

Both. They catch different attacks — a burst from one client, and a slow grind from many — and a vendor offering one as though it were the other has not thought about the second.

How many previous passwords do you check against?

A good answer sounds like

A number, and a reason for the cap.

What ours actually is

Up to twenty-four, capped regardless of policy because each comparison is a deliberately slow hash computation on a form submit.

What happens to an account whose password age you do not know?

A good answer sounds like

A stated choice, either way.

What ours actually is

Treated as not expired, because a missing value is a data gap rather than evidence about a password. Worth asking of any system enforcing expiry — the opposite choice locks people out for a column that was never filled in.

Our take

The floor is the part of this we would argue for hardest, and it is the part that costs the most in support conversations. A security setting that turns both ways transfers the decision to whoever is most annoyed by it, and the vendor keeps the reputation of a good default while shipping whatever the customer chose. Making the default a floor means taking the position and defending it. Everything else here follows a similar shape: count against the address rather than the account, because the alternative leaks; cap the history, because the honest constraint is a slow hash and not a policy preference; treat a missing date as unknown rather than as bad, because a control that punishes people for a data gap gets disabled. None of these is clever. All of them are decisions somebody has to make on purpose, and the ones that fail quietly are the ones nobody made at all.

The sign-in policy ledger, precisely

What AWRA OpsHub does today

  • A password minimum length that behaves as a floor: a workspace can raise it above the baseline of twelve and no configuration produces anything shorter.
  • Mixed case, numbers and symbols applied unconditionally, with no branch in the code that can switch complexity off.
  • Password history comparing a new password against previous hashes, capped at twenty-four regardless of policy because each comparison is a deliberately slow computation on a form submit.
  • History storing the hash taken from the account record rather than a fresh hash of the plaintext, so what is retained is exactly what was in use.
  • Password expiry per workspace, with an unpopulated last-set date treated as unknown rather than as old, so a data gap does not lock out an account that did nothing wrong.
  • An account lockout counted against the address typed rather than the account found, so an address with no account locks identically and the control cannot be used to discover who has an account.
  • That counter keyed on a hash of the address, lowercased and trimmed first, so no readable list of attempted addresses is stored and casing cannot split one counter into several.
  • An attempt window equal to the lockout duration, so scattered typos over months do not accumulate into a lockout for a long-standing user.
  • A route rate limit retained alongside the lockout, because a burst from one client and a slow grind from many are different attacks.
  • Counters held in the shared cache rather than a table, so a workspace has one lockout across every application server and a mistyped password is not a database write.
  • A workspace with no threshold configured never locked, with the counter not even read.

More we can add to your workspace

  • A check against known-breached password lists, so a password that meets every length and complexity rule and appears in a public breach corpus is refused on that basis.
  • Lockout counters that survive a cache clear, for the deployments where an evaporating lockout is not an acceptable trade.
  • A notice to the account holder when their address is locked out, which is often the first anybody knows that their address is being guessed at.
  • A progressive delay before the lockout threshold, so early attempts slow down rather than the control being entirely off and then entirely on.
  • Workspace-visible reporting on lockout activity, showing which addresses are being attempted and how often.
  • A floor on the lockout threshold and duration, matching the treatment the password length already gets, so those two settings cannot be configured into ineffectiveness.

Where we point you to a specialist

  • We hold that the password minimum stays a floor rather than becoming a two-way setting, and we will keep declining the request to lower it. A dial that turns both ways relocates the decision to whoever finds it most inconvenient, and the vendor then describes the outcome as a customer choice.
  • Where an organisation is subject to a standard specifying particular authentication controls, that standard governs and we will implement what it requires rather than presenting our baseline as equivalent. Establishing which standard applies is a question for your own advisers.
  • We would decline to make sign-in respond differently to an address that has an account and one that does not, in any part of the flow, even where the different response would be more helpful. Helpfulness there is a directory, and we would rather the error message be slightly less useful to everybody.

A breached-password check and a notice to the account holder on lockout are the two that add the most for the least disruption, and neither disturbs the floors described above. Both are contained pieces of work.

Scope, not a ceiling

Three additions that keep every floor in place

The policy objects above are the enforcement points, so extending them is a matter of adding a check rather than restructuring a flow.

Refusing a breached password

Length and complexity say nothing about whether a password is already in a public corpus, and the most commonly stuffed credentials pass every rule here.

Telling the account holder

A lockout is usually the first observable sign that somebody is guessing at an address, and today it is observable to us and not to them.

A delay before the threshold

Turning the control from off-then-on into something that degrades gradually, which slows an attacker well before the lockout and inconveniences a typo far less.

We publish scope, not dates.

Scope sign-in hardening

Try the enumeration question on whatever you sign into

Pick an address at your own organisation that certainly has no account, and get the password wrong six times. If the response differs from doing the same against a real address, the lockout is telling anybody who asks which of your people have accounts. It is a two-minute test and it is rarely run.

Talk through sign-in policy

Frequently asked questions

Can we set our password minimum lower than twelve characters?

No. The rule takes whichever is larger of the configured value and the baseline, so setting six produces twelve and setting sixteen produces sixteen. Complexity is stricter still: mixed case, numbers and symbols are applied unconditionally and there is no branch in the code that could switch them off. This is a deliberate position rather than an oversight, and it is the one setting here we would decline to make two-way.

Why count failed sign-ins against an email address rather than an account?

Because counting against an account means an address with no account behaves differently from one that has one, and that difference tells an attacker which addresses are real. A confirmed list of accounts is the starting point for credential stuffing, password spraying and targeted phishing. Counting against the address typed makes both cases identical, so the lockout stops being a way to discover who works at your organisation.

What happens to an active lockout if the cache is cleared?

It is cleared with it, and that is a known trade. The alternative is a database write on every mistyped password, which hands an attacker a free write endpoint they can drive at will. In this deployment the cache runs on the shared database, so counters do hold across application servers — a workspace has one lockout rather than one per machine.

Why is password history capped at twenty-four?

Because a password hash is deliberately slow to compute, and a new password has to be tested against each retained hash individually — stored hashes cannot be compared with one another, only tested against a candidate. Twenty-four is twenty-four slow computations while somebody waits on a form submit. Setting the policy higher does not extend the check; it is capped in code so the form stays usable.

Is an account with no recorded password date treated as expired?

It is treated as not expired. A missing value is a gap in the data rather than evidence about the password, and forcing a reset on that basis locks out accounts that have done nothing wrong. The security-maximalist reading would treat unknown as worst-case, and the counter-argument taken here is that a control which punishes people for an unpopulated column gets switched off, and a control that is switched off protects nobody.

Do you have rate limiting as well as lockouts?

Both, and they catch different things. A route rate limit counts requests from one client and stops a burst; it is blind to five attempts an hour from each of two hundred addresses. An account lockout counts failures against one address from anywhere at all and catches exactly that pattern. Neither substitutes for the other, and a system offering one as though it covered both has not considered the second.

Help Center

Need a quick answer while you read?

Run inventory, procurement, assets, sales, and field work with approved AWRA guidance for setup, migration, integrations, security, pricing, and support.

Search all approved AWRA public help articles.

Open Help Center