Dev.to Security 🔐 Cybersecurity 👁 0 📖 11 min read

Multi-Tenant E-Commerce Security: Preventing Cross-Customer Data Exposure in Shared Applications

By Vineet Arora, Software Engineer, Backend Development, and Application Security OWASP’s own example of the top API risk comes straight from e-commerce. A platform hosts online shops, and one API path includes the shop

Multi-Tenant E-Commerce Security: Preventing Cross-Customer Data Exposure in Shared Applications

By Vineet Arora, Software Engineer, Backend Development, and Application Security
OWASP’s own example of the top API risk comes straight from e-commerce. A platform hosts online shops, and one API path includes the shop name. An attacker swaps in a different name and reads revenue data belonging to other shops. No exploit code is needed. Just a modified request the server should have refused.
Here’s a hypothetical. A staff member at Merchant A changes one digit in an order URL and gets back Merchant B’s order, complete with a shopper’s name, address, and purchase history. This isn’t a real incident, but it shows how thin the line is between a working feature and a data leak.
Sharing infrastructure isn’t the problem. What matters is whether every access boundary gets enforced on every path that touches tenant data. Below, we’ll walk through architecture, controls, test scenarios, and how to fix what you find.


What Cross-Customer Data Exposure Looks Like
Exposure happens when a request from one tenant works against another tenant’s data. The usual causes are an endpoint that trusts an ID sent by the client, a query with no tenant filter, or a cache that hands back the wrong response.
In a multi-tenant application, a valid login does not establish ownership of every resource the user can identify. The server must verify that the requested order, customer record, or store belongs to a tenant the user is authorized to access.
OWASP’s 2023 API Security Top 10 puts Broken Object Level Authorization at number one. Four terms are worth knowing:
BOLA: an endpoint takes an object ID and acts on it without checking whether this caller is allowed to access that object.
IDOR: the older web term for the same pattern.
BFLA: a caller reaches a function they shouldn’t, like an admin refund action. OWASP draws the line this way: reaching a forbidden endpoint is BFLA, and tampering with an ID is BOLA.
BOPLA: a caller reads or writes properties they shouldn’t, such as cost price or internal notes. OWASP created this category by merging the old Excessive Data Exposure and Mass Assignment risks.
Two things are at risk: confidentiality, when data gets read, and integrity, when orders, prices, or settings get changed. Depending on the data, the fallout could include privacy obligations, contract disputes, or lost customer trust, though none of that is guaranteed. Also, not every authorization bug crosses a tenant boundary. A cashier who can issue refunds in their own store has a role problem, not a tenant problem.
Define Tenants, Users, and Data Ownership
You can’t enforce a boundary you haven’t defined. In this hypothetical platform, the tenant is the merchant account. The roles around it each need their own definition:
Merchant: the business customer and tenant owner.
Store: a storefront that belongs to one merchant. Decide whether one merchant can own several stores.
Shopper: the merchant’s end customer, scoped to a store.
Employee: Merchant staff whose data access must remain within the merchant accounts they are assigned to. For employees serving multiple merchants, validate the selected merchant against their actual membership before retrieving or modifying any records.
Support agent and platform operator: internal roles with broad reach, so they need the strictest controls.
Every resource needs one clear owner. When sharing is legitimate, like an agency managing several merchants, make it explicit, record it, and make sure it can be revoked.
Never use a tenant identifier supplied in a request as the sole basis for selecting records. Resolve the merchant through a trusted server-side membership relationship, then apply that verified tenant scope to database queries and downstream services. This prevents a user from accessing another merchant's records simply by changing a URL parameter, header, or request payload.
Tenant Isolation Starts With Architecture
AWS’s SaaS guidance describes three patterns. In a silo, tenants get dedicated resources. In a pool, they share. A bridge mixes the two. For databases, that roughly means a database per tenant, a shared schema, or a hybrid like a schema per tenant inside a shared database.
Bridge is more flexible than “big tenants get their own database.” AWS’s example has a shared web tier above separate application tiers and storage per tenant, and each service can choose silo or pool on its own.
Each option trades isolation strength against operational effort:
Shared schema: cheapest to run and migrate, but isolation depends on filters and policies being right everywhere.
Schema per tenant: stronger logical separation, but migrations get heavier as the tenant count grows.
Database per tenant: the clearest boundary and easy per-tenant restores, but it costs more.
Dedicated storage doesn’t make everything secure. AWS points out that a silo still relies on shared identity, onboarding, and operations. Its older whitepaper, now marked historical, adds that management and agility drop as the number of silo VPCs grows. And a database per tenant can still leak if an admin API or shared cache ignores the boundary.
Base the choice on your actual requirements: contract terms, data sensitivity, how many tenants you have and how big they are, noisy-neighbor risk, restore needs, and team capacity. Weigh all of that against a realistic threat model. There’s no single right answer.
Where Cross-Tenant Access Breaks Down
Your attack surface depends on your architecture, and few applications have every component listed below. Walk through the data paths you actually have.
Lookups and lists: direct IDs, nested resources, search endpoints, and pagination cursors.
Bulk operations: batch updates, multi-order actions, imports, and exports.
Object properties: response fields that expose too much, and mass assignment, where a client sets fields it shouldn’t.
Database layer: missing tenant filters, misconfigured RLS, and service accounts with too many privileges.
Secondary storage: caches, search indexes, object storage, downloads, and generated reports.
Webhooks and async work: tenant mapping, queues, retries, and background workers.
Tenant membership changes: removed employees who can still retrieve merchant records, users whose multi-merchant assignments have changed, and requests that continue using outdated tenant associations.
Support and admin tools: cross-tenant lookups, impersonation, and internal APIs.
The UI point is worth spelling out. Hiding a button doesn’t stop anyone from calling the endpoint behind it. Bulk and export paths need extra attention too. Teams often lock down the single-record view and forget the endpoint that returns hundreds of records at once.
How to Enforce Tenant Isolation
Enforce tenant ownership before allowing an operation to proceed. For each request, resolve the merchant associated with the authenticated user, constrain the requested resource to that merchant, and verify that the resource exists within the permitted tenant scope. Reject requests that attempt to reference records outside that boundary, even when the supplied object ID is valid.
Apply tenant restrictions consistently at the data-access layer rather than relying on individual developers to remember filters in every endpoint. Repository methods should require a validated tenant context, while API responses should expose only the fields needed for the requested operation. Review shared database connections and service integrations to ensure that one merchant's request cannot retrieve or modify another merchant's records.
PostgreSQL row-level security makes a strong second layer, and its documentation is upfront about the gaps. Once RLS is enabled, a table with no policy denies all normal access by default. But there are exceptions:
Table owners normally bypass policies unless you run ALTER TABLE ... FORCE ROW LEVEL SECURITY.
Superusers and roles with BYPASSRLS always bypass them.
Operations that apply to the whole table, such as TRUNCATE and REFERENCES, aren’t subject to row security.
If your application connects as the table owner, RLS may not be enforcing anything. Use a dedicated, unprivileged application role, test the policies directly, and keep authorization checks in the application as well.
Verifying Webhooks and Background Jobs
Webhooks and queues run without a user session, so you have to rebuild the tenant context on purpose. The principles apply to any provider. The details below use Stripe’s documentation as an example.
Start by verifying that the request is genuine, using the provider’s documented method. Stripe signs the raw request body, so any framework that alters the body before verification breaks the check. Its libraries also reject timestamps more than five minutes old by default, which helps limit replay attacks.
A valid signature proves where the event came from. It doesn’t prove the action is allowed. Map the event to a tenant using a record you control, then authorize the action separately. Stripe’s own Connect sample looks up the event’s account in a server-side map and returns an error when no match exists. That’s the right way to fail: reject or quarantine the event and raise an alert. Don’t guess.
Expect duplicates and events arriving out of order. Stripe says endpoints may receive the same event more than once and recommends logging processed event IDs. It doesn’t guarantee event order, and it retries failed live-mode deliveries for up to three days. If a tenant mapping changes during that window, you’ve got a real edge case, so decide ahead of time how stale mappings are handled.
Finally, carry tenant context into queues, jobs, exports, caches, and generated files. A worker that loses track of the tenant is a cross-tenant bug waiting to happen.
Build a Practical Cross-Tenant Test Matrix
Start with the actors: two tenants, each with an owner and a limited staff user, plus one user who belongs to both. Then list the resources and permissions each one should and shouldn’t have.
In an environment where you’re authorized to test, run normal operations alongside deliberate unauthorized attempts. Cover reads, writes, deletes, property changes, bulk actions, exports, files, webhooks, and jobs. Add membership changes, session revocation, and regressions after migrations. OWASP’s guidance for BOLA is simple: write tests that evaluate the authorization mechanism.
For every case, write down the expected result, what actually happened, the evidence, and any gaps in coverage. Passing tests only show that those paths held up on the day you ran them.
Prioritize and Remediate Findings
Reproduce the finding and confirm it’s real.
Work out which resources, actions, tenant boundaries, and data paths are affected.
Assess how exploitable it is, how sensitive the data is, whether it allows reads or writes, what privileges it needs, how far it reaches, and what it means for the business. If your organization requires a score, use an established method like CVSS.
Name the root cause, such as a missing ownership check, and the control that fixes it.
Apply the fix, retest the original case, and add a regression test.
Deploy carefully, confirm the fix works, and document any remaining risk.
Logging, Monitoring, and Incident Response
Signals worth watching include authorization denials, repeated failed object lookups, unusual bulk reads, and unexpected spikes in exports. Treat them as leads, not proof. Plenty of denials are just honest mistakes, and logging on its own doesn’t prevent anything.
Keep tokens, secrets, and unnecessary personal data out of the logs, and protect the logs themselves. If you suspect an exposure, figure out the scope, contain it, preserve the evidence, and recover. Check what your contracts, policies, and the applicable laws require you to report, and bring in legal counsel.
Maintaining Isolation Through Application Changes
Verify that every new endpoint applies the correct merchant scope to reads, writes, and deletes.
Validate tenant ownership after schema changes, data migrations, and record transfers.
Test whether backup restoration preserves merchant-to-record relationships.
Check that merchant exports, generated reports, and file downloads cannot include another tenant's data.
Revalidate access when employee-to-merchant assignments change.
Review cross-tenant access paths in shared services, support tools, and administrative workflows.
Record unresolved isolation issues, their business impact, and the team responsible for fixing them.
Frequently Asked Questions
Is a shared database safe for a multi-tenant e-commerce application?
It can be, if your controls and requirements support it. Choosing a schema doesn’t make it safe on its own.
Is row-level security enough to prevent cross-tenant data exposure?
No. RLS adds real protection, but you still need to check the owner and BYPASSRLS exceptions, your application-level authorization, and paths outside the database, like caches and files.
Do UUIDs prevent IDOR or BOLA?
No. OWASP notes that object IDs can be sequential integers, UUIDs, or strings. IDs that are hard to guess make enumeration harder, but they don’t authorize anything.
How should teams test tenant isolation?
Run authorized tests that check both what should work and what should fail, across roles, actions, and data paths. Keep them as regression tests, and collect evidence.
Do web and mobile applications need separate tenant-isolation controls?
No. Restrictions in the client aren’t authorization. Both apps call the same backend, and the backend has to enforce permissions itself.
When should a team consider an external tenant-isolation assessment?
Consider an independent assessment when your application supports multiple merchants, uses shared infrastructure, has complex cross-tenant integrations, or handles sensitive customer and transaction data. The review should test actual merchant boundaries across APIs, databases, background jobs, and shared services.
Planning a Tenant-Isolation Review
Define the application scope and tenant model.
Map identities, roles, resources, and data paths.
Agree on testing boundaries, including what’s allowed in production and what’s limited to non-production.
Set expectations for evidence, reporting, and remediation ownership.
Plan retesting and spell out the limitations.
Tenant isolation isn’t a feature you ship once and forget. You have to keep proving it as the product changes. If you’d like an independent look at where your boundaries hold up, our team can help scope a tenant-isolation review around your architecture and risk profile.

About the Author
Vineet Arora, Software Engineer, Backend Development and Application Security
Vineet Arora is a software engineer who mostly works on backend development. He spends his time on PHP, API integrations, and databases and pays close attention to website speed and security. In this article, he looks at where access checks tend to go missing in shared online stores and how to catch those gaps early.

Build Safer Shared Platforms
If your store or marketplace serves more than one merchant, it’s worth checking how well each merchant’s data is kept apart. Techosquare offers web development and software engineering services that can help you plan, build, or review that part of your application. You can learn more at Techosquare.com.

📰 Read the original article on Dev.to Security

Originally published by Dev.to Security. Aggregated on AIWithGhost for educational purposes — full credit and traffic to the original publisher.