Engineering & deliveryFoundation
Tenant separation that survives the security questionnaire
3 min read
In SaaS teams the moment is almost always the same. The product runs, smaller customers are happy, and then the first serious enterprise prospect arrives — with a security questionnaire containing a question nobody asked before: how do you ensure our data is separated from other customers' data?
In many cases the honest answer is: by a condition in application code. And that is exactly the answer that puts the deal at risk.
Why the filter is not enough
Technically WHERE tenant_id = ? works fine. As evidence it does not, for a simple reason: isolation depends on every single query in the system carrying that condition. With fifty queries that is manageable. With five hundred, spread across reports, background jobs, exports and an API, it is a matter of time.
And one forgotten filter is not a bug but a reportable data breach. So the questionnaire does not want to know whether you work carefully. It wants to know what happens the one time someone did not.
The four usual models
Filter in the application. Cheap, fast, everywhere. Isolation is a matter of discipline. Acceptable for small customers, rarely for regulated industries.
Row level security in the database. The condition moves to where it cannot be forgotten. A clear jump in provability, because separation no longer depends on the caller.
Schema per tenant. Clear boundaries, easy to explain, individually backed up and restored. The price is operational effort: migrations run n times.
Database or instance per tenant. The strongest form, usually for a few large customers. Expensive if applied to everyone.
In practice the hybrid model almost always wins: many small tenants together but isolated at the database level, and individual large customers physically separated. What matters is that moving between the two is a configuration decision, not a redevelopment. Build it at the second enterprise customer and you build it twice.
What the questionnaire really checks
Behind the wording sit four questions, and no architecture diagram answers any of them:
- Can an application bug expose another tenant's data?
- Can you prove who had access at a given point in time?
- Can you delete or export a single tenant without touching the others?
- Do you use our identity system, or do we have to hand you user accounts?
The last one is underrated. Enterprise customers want their own accounts, their own groups, their own way to revoke access — SSO is not convenience, it is their security process. That is why identity and tenant separation belong to the same decision; with Keycloak it can be built so you keep control instead of handing it to a vendor.
Permissions need a history
The point that has to be supplied late in almost every audit: it is not today's permission model that counts, but the state back then. Who had access six months ago, who granted it, when was it revoked? If permissions are stored only as current state, that answer does not exist — the same gap as with tamper-proof logging.
The pragmatic path
Not everything at once. A sensible order: enforce separation at the database level so a forgotten filter stays harmless. Then connect identity properly so SSO and revocation sit with the customer. Then historise permission changes. After that the questionnaire largely answers itself — and the second enterprise deal costs nothing new.
Where your system stands today is answered by the provability check in ten questions. What it turns into during an audit is covered in the implementation view on NIS2.
← Back to insightsArticles on this topic
Engineering & deliveryNote
Bolt-on logging: where is the truth?
AI takes the burden of human vagueness off us. So why do we place a second vagueness — a log — next to our core processes?
Engineering & deliveryField notes
Tamper-proof logging: why database logs do not count in an audit
Almost every system logs. Audits still fail on the question of who changed what and when. The difference between a log and evidence — and what creates it.
Engineering & deliveryField notes
Production readiness in the AI factory: what to check when agents write the code
Requirements are agent-readable, the arc42 is maintained, the pipeline is green. The old question — does it run? — has become trivial. The new one: how do we know a change is right when nobody read every line?