← All updates

Security / Field note

Your tenant filter can be right and your SQL still wrong

Authorization and query construction solve different problems. A tenant condition cannot rescue a query that lets input become SQL syntax.

By Zyel · Published · 3 min read

The application checks the customer, builds a query containing the customer identifier and asks the database for an invoice. On paper, the access rule sounds reasonable. The risk lies in how the rest of that query was assembled.

If a value from a request is pasted directly into SQL text, the database may interpret part of that value as query instructions. A carefully chosen tenant condition can then sit inside a larger expression whose meaning no longer matches the developer's intention. A good fence is less reassuring when the gate can rewrite the property map.

Two decisions need two controls

Authorization asks whether a user may reach a record. Safe query construction keeps supplied data from changing the database instruction. Both need to work. Parameter binding does not establish customer membership, and a customer-membership check does not make concatenated SQL safe.

OWASP recommends prepared statements with parameterized queries as a primary defense for data values. PHP's PDO documentation describes the corresponding separation between the SQL statement and the values supplied for its parameters. The database can then handle an invoice identifier as an identifier value, including ordinary punctuation, rather than as an extra fragment of SQL. [1] [2]

Ordinary data exposes the same design problem

Security demonstrations often use unusual input, which can make the defect feel remote from everyday business work. Ordinary apostrophes are a useful reminder that data has a life outside a developer's assumptions. Names, reference numbers and imported descriptions do not promise to avoid punctuation.

A robust lookup should handle legitimate values correctly while rejecting access outside the authenticated customer's permissions. Merely filtering out a few suspicious characters can damage valid data and still leave unexamined query paths. The design goal is an explicit boundary between instruction and value.

Some query parts are structure

Parameters generally represent values. A requested sort order, column name or table selection is a structural choice, and it needs a different treatment. An application can map a small set of allowed user-facing choices to fixed query fragments that it owns. Arbitrary request text should not become the name of a database object or an ordering expression.

That distinction matters in reporting screens, where the simple invoice lookup has often evolved into a flexible builder. Optional filters, exports, administrative searches and legacy helper methods deserve review alongside the most visible endpoint. One safe method does not automatically repair every query in the application.

A regression result should include legitimate work

In a disposable training database, a useful check compares several outcomes: the requesting customer's normal invoice is returned, another customer's invoice is unavailable, a fixed synthetic syntax canary does not change the lookup's meaning, and a legitimate value containing an apostrophe still works.

The value of that test lies in its repeatability and scope. It establishes a specific behavior in a controlled fixture. It does not establish that a particular customer application contains the defect, and it should never require experimenting against an unrelated public service. Production assessment needs its own authorized scope and careful evidence handling.

Keep the other layers doing their jobs

Least-privilege database access can reduce what an application account is allowed to do. Application logging can help explain unusual failures. An edge security product may reject some malicious requests. These layers have useful roles, but the query itself still needs to preserve the instruction-and-value boundary.

After a repair, retest the valid workflows, the denied workflows and the unusual-but-legitimate data. Record the database driver and execution environment as well as the result. A demonstration passing in one engine is useful evidence for that fixture; the production driver and its real query paths still need their own verification.

The practical lesson is reassuringly concrete. Keep identity decisions in trusted application context, keep allowed query structure under application control, and send data through value parameters. Each part has a clear job. That clarity makes the code easier to review and much harder to misunderstand later.

Sources + further reading

Source dates describe the original material. Publication above is the date this Zyel article went live.

  1. SQL injection preventionOWASP · Checked Nov 4, 2026
  2. PDO prepared statementsPHP · Checked Nov 4, 2026

Reader discussion

What would you ask next?

Questions, useful corrections and thoughtful disagreement are welcome. Comments are reviewed before they appear.

No reader comments yet.

10–2,000 characters. Plain text. Leave out passwords, private project data and personal contact details. A blank display name appears as Anonymous.