Serguey Asael Shinder: One null for unset and for allow nothing is a security bug waiting to happen
The PostgreSQL JDBC driver fixed a vulnerability this week that is worth reading for its shape rather than its details. In GHSA-rhp9-mr79-r74h, a requireAuth setting that excluded every authentication method the driver knows was enforced as no restriction at all — so the driver accepted whatever the server asked for, cleartext password included.
The advisory gives the root cause in one sentence: the parser returns null both when the property is unset and when its value leaves no method allowed, and the check treats null as "no restriction". The value that should allow nothing therefore allowed everything.
Why this keeps happening. "Not configured" and "configured to the empty set" are opposites in a security setting — one means use the default policy, the other means deny all. But in code they are both "nothing", and the cheapest representation of nothing is null, an empty list, or zero. Once both cases collapse into the same value, the code downstream can only pick one interpretation, and it almost always picks the convenient one: if there is nothing to check, do not check.

The same collapse shows up elsewhere:
- an empty allow-list that is read as "allow all" because an empty list means "the user did not set one";
- a zero timeout that means "no timeout" in one library and "fail immediately" in another;
- an empty set of scopes on a token that a resource server treats as "unscoped, full access".
The design rule. Make "unset" and "empty" different values that cannot be confused:
- Represent "unset" by absence —
Optional.empty(), a missing key, a dedicatedUNSETconstant — and "empty" by an empty collection. Never let a parser return the same thing for both. - For security settings, give the empty set its literal meaning: deny everything. The pgjdbc fix does exactly this, and the advisory notes the consequence honestly — such a configuration now refuses every connection, which is correct, because nobody sets it on purpose.
- Fail closed on parse problems. A value that parses to nothing should be an error, not a default.
The test that would have caught it. For every security option, write the two boundary cases as separate tests: option absent and option present but permitting nothing. If both pass with the same behaviour, one of them is wrong.
The deeper lesson in the advisory is its last line about impact: on affected versions, the misconfigured connections worked normally, which hid the mistake. A setting that silently does the opposite of what it says is worse than one that breaks — the broken one gets fixed.