Security 6 min read

What your error message tells an attacker

Every system that refuses someone has to decide what to say. It gets treated as copywriting, and it is frequently the most consequential security decision in the flow.

A single illuminated access control panel showing a refusal on a dark wall, illustrating what an error message discloses to an attacker

Every system that refuses someone has to decide what to say. It is treated as a copywriting problem and it is a security decision, frequently the most consequential one in the whole flow.

There are two conventional positions and both of them are wrong most of the time.

The first is to explain. Tell the person what went wrong so they can fix it. Support costs fall, users stop raging, and for the enormous number of systems whose failures are not adversarially interesting this is simply correct.

The second comes from password authentication, where the discipline is absolute. Never say whether the username exists. Never say whether the password was close. Answer every failure with the same words, because anything else tells an attacker which half of the guess to keep. This is right, it is decades old, and it is drilled into everyone who has been near a security review.

The mistake is thinking these are the only two options, and then applying the second one everywhere the stakes feel high.

Where the password rule stops working

The password rule is built on an assumption that is worth making explicit: that every failure is equally useful to an attacker. With credentials that holds. Knowing the username exists is a genuine advance for someone guessing.

Now consider a different kind of gate. Access to a resource conditioned not on who you are but on the circumstances of the request: where you are, when you are asking, which device you are on, which network it is attached to, whether you are assigned to the thing at all. Attendance systems work this way. So do site access, shift verification, and any code that has to be redeemed in a particular place.

Refuse one of these and you have several possible reasons, and they are not equally useful to somebody probing.

Consider being told you are outside the permitted area. That is a specific, actionable piece of information, and an attacker can act on it immediately and at no cost. They move fifty metres and try again. Then a hundred. Within a few attempts they have mapped the boundary of the geofence, and they have done it without triggering anything that looks like an attack, because every request was well formed and politely refused.

Compare that with a password prompt. You cannot move fifty metres closer to a password. There is no gradient to follow. That is exactly why uniform errors work there and why the analogy silently breaks here.

The property that actually matters

The question is not how sensitive a failure sounds. It is whether telling somebody about it hands them a usable oracle.

An oracle, in this sense, is a thing you can ask repeatedly, cheaply and with a signal that improves your next attempt. Geofence boundaries are an oracle. Time windows are an oracle: refused at 08:58, accepted at 09:00, and now you know the schedule. Whether a particular person is assigned to a particular site is not really an oracle, it is a single fact, but it is a fact about somebody else and it should not be handed to a stranger.

And then there are failures where naming the reason gives an attacker nothing at all.

Liveness is the clearest case. If a face check fails, telling the user so is close to costless, because a genuine user in poor lighting and an attacker holding up a photograph are in the same position: both have to try again with a better presentation, and neither learns anything from the message that they did not already know. The failure is not informative because the attacker already knows what they attempted.

So the useful partition is not by sensitivity. It is by probeability. Which failures can be probed for gradient, and which cannot.

Selectively uniform

That produces a design that looks inconsistent from the outside and is precisely reasoned underneath. Some failures are named. Others collapse into one indistinguishable response. The partition is by whether disclosure creates a usable oracle, not by how bad the failure sounds.

This is the subject of a patent application I filed this year, GB2619020.7, on selectively uniform denial of a context-gated access request according to the probeability of each failed condition. Probeability is a coined term and I am aware it will attract questions. It names the property the design turns on, which is the degree to which naming a failed condition gives a probing attacker a signal they can act on.

Two things have to remain true for this to be workable in production.

The internal record stays fully differentiated. Uniformity is what the requester sees, never what the system knows. If your logs cannot tell you which condition failed, you have not built a security control, you have built an outage you cannot diagnose.

The operator gets the specific reason. The person running the site needs to know that eleven refusals in the last hour were all geofence failures from the same device drifting around the perimeter. That is the signal that an attack is happening, and it is invisible if you have flattened it for everybody.

Why the blanket approach is worse than it looks

Uniform errors have a cost that rarely gets counted. Every legitimate user who cannot see why they were refused becomes a support ticket, and every support ticket is a human being who can be socially engineered into explaining the thing the error message would not.

I have seen this play out. A system that refuses to say why sends people to a help desk, and the help desk, trying to be useful, says "you need to be on site to clock in". The oracle was not removed. It was relocated to somebody with a headset and a target for call handling time.

It is the same trap I have described in biometric verification, arriving by a different route. There the cost lands on genuine users and the control gets loosened in configuration. Here it lands on the support queue and the control gets loosened by a person trying to be helpful. Either way what survives is the shape of a security decision without its substance.

What I would do

Go through your failure conditions and ask one question of each: if somebody could trigger this a thousand times, would the answers converge on something they want?

If yes, that failure joins the uniform set. If no, name it, and get the support cost reduction for free.

Do it condition by condition rather than screen by screen. The instinct to make the whole flow consistent is a design instinct, and here it is the wrong one. Consistency is what you owe the user's understanding. It is not what you owe an attacker's search.

Most systems have never made this decision deliberately. They inherited an error message from whoever wrote the first version of the endpoint, and it has been quietly telling people things ever since.

Common questions

Why do login forms never say whether the username exists?

Because it hands an attacker half the answer. If a system distinguishes an unknown username from a wrong password, an attacker can confirm which accounts exist and concentrate guessing on those. Uniform failure messages remove that signal, and for credential authentication this is correct and long established practice.

Why is a uniform error message the wrong default for location checks?

Because location can be probed for a gradient. Told they are outside the permitted area, an attacker moves fifty metres and retries, then a hundred, and maps the boundary in a few polite, well-formed requests. You cannot move closer to a password, so the analogy that justifies uniform errors for credentials does not carry across.

What is probeability?

A coined term, from probe-ability, for the degree to which naming a failed condition gives an attacker a signal they can act on. It is the property worth partitioning failures by: a condition that can be probed repeatedly and cheaply for an improving signal should be answered uniformly, and one that cannot can safely be named.