The commonest flaw in client portals is invisible to every scanner
A client portal. A real user, properly logged in, opens one of their documents. The address is something like /api/documents/4187.
They change the number. 4186. And they see another company’s signed contract.
It is the top entry in the OWASP API list, it is called Broken Object Level Authorization, and we find it in a good proportion of the portals we get called about.
Why no scanner finds it
Because technically there is no error. The program does exactly what it was asked: it was asked for document 4186, and it returns document 4186. It does not crash, it produces no warning, it answers 200.
An automated tool looks for known patterns: unfiltered input, old versions, bad configuration. It cannot know that the document does not belong to whoever is asking, because that fact lives in your business model, not in the code.
How it gets written by accident
Like this:
if (!user_is_logged_in()) {
return null;
}
return db()->row('SELECT * FROM documents WHERE id = ?', [$id]);
There is a check, and it is the one everybody expects to see: no login, no entry. But the question the last line answers is “does document 4186 exist?”, not “does document 4186 belong to whoever is asking?”.
How it gets closed
$user = user_is_logged_in();
if (!$user) {
return null;
}
return db()->row(
'SELECT * FROM documents WHERE id = ? AND client_id = ?',
[$id, $user->client_id]
);
The fix does not add a check: it removes the possibility of forgetting one.
As long as authorisation is an if written next to the query, sooner or later someone will write a new query without that if beside it — maybe in two years, maybe a different supplier. Put inside the query, the wrong question can no longer be asked.
And there is a useful side effect: anyone trying a random number gets a 404, so they do not even learn that the number corresponds to anything.
How to check yours
It takes a person, two test accounts and half an hour. Open something with the first account, take the address, and try it again with the second. If you can see the first account’s document, you have found it.
You do not need to be technical to run that test. You only need to run it.
$ ls ./journal --altri
Read next
Client portals: when they pay off and when they are a waste
Non è una questione di dimensione dell'azienda. È una questione di quante volte alla settimana rispondi alla stessa domanda.
What does a lead cost you? If you do not know, you are spending blind
Il conto è una divisione. Il difficile non è farla: è avere i due numeri giusti, e quasi nessuno ce li ha.
A quote given before the prototype is a bet
Perché chiediamo di costruire le schermate principali prima di dire quanto costa tutto il resto — e perché conviene anche a chi paga.
