The audit came back clean
on everything but one line.
Every query in the service
used bound parameters.
No string building.
No quotes glued together.
The team was proud of it,
and they had earned it.
The finding was in the product list.
The page let you sort,
by name, by price, by date,
and the column came from the link.
sort equals price.
That one went into the SQL as text.
Somebody had tried to bind it,
of course.
It did not work.
A parameter is a value,
and a column name is not a value.
Bind the word price
and the database
sorts by the string price,
the same constant on every row,
and the order quietly stops changing.
So the column was glued in,
with a clear conscience,
because it was only a sort.
The tester did not send price.
He sent a CASE expression.
If the first character
of the admin's password hash
is an a, sort by name,
otherwise sort by price.
The page never showed an error.
It never showed the hash.
It just came back
in one order or the other.
One request,
one yes or no.
A script asked
a few thousand questions
and read the answers
out of the shape of the list.
That is blind injection,
and the order of the rows
is as good a channel
as any message.
Here is the rule underneath it.
Parameters protect values.
They do nothing
for the structure of the query.
Column names.
Table names.
The sort direction.
All of that is code,
and code from the user
is the whole problem.
So do not pass it through.
Map it.
Keep a small table
of the words you accept,
each pointing at a column
you wrote yourself.
Name goes to name.
Price goes to price.
Anything else gets the default.
Direction is asc or desc,
chosen by you,
never copied.
Then search for the other places.
ORDER BY.
GROUP BY.
A table picked per tenant.
A column picked by a report builder.
They all look harmless,
because none of them
carry data.
They carry instructions.
– Serguey Asael Shinder
Top comments (0)