1

Goal

Allow single web service instance to serve different tenants on a per request basis.

Design

simplified design

In order to allow a single web service instance to handle requests for multiple tenants there exists a tenant context service which maps a JWT to database credentials (simplified for the purpose of this question). These credentials can then be used to retrieve only the authorized tenants data from the database.

Problem

There seem to be several ways in which a small and non-malicious change to the codebase by a developer could lead to data-leaks between tenants down the road. Example scenarios:

  1. Any use of the singleton pattern that effectively caches constructed components between requests could lead to implicitly cached credentials/session. This could for example be introduced by changing a flag in the dependency injection container. I could see both developer and reviewer being unaware of the consequences here as this is not entirely obvious.
  2. Storing the JWT, the db credentials or the session created from these credentials in a static property.
  3. Storing results from the database in-memory in any components which might leak them between tenants.

Questions

  1. Is there any best practice on how to handle this problem? Does it require changes to the design?
  2. Is there a way to reliably test against such a bug (it seems non-trivial as it could be edge cases in large scale deployments leading to this issue)
  3. Would it make sense to establish some kind of memory sandbox per request in order to avoid this and if yes, is there any documentation or example on how this can be done or is usually done?
1
  • Please edit the question to limit it to a specific problem with enough detail to identify an adequate answer.
    – Community Bot
    Jan 31, 2022 at 15:41

1 Answer 1

1

I would say the standard approach is to have a single db connection using a service user for the db and put the security in the code layer.

The jwt has a tenant id embedded, or its looked up based on the user id, and this is passed through to database queries as a where clause. ie select * from customer where tenantId='{id}'

This is simple, testable and any weird problem's which means the wrong tenant's data is served, such as caching etc would also result in errors for a single tenant.

Adding multiple connections with what you hope is correctly mapped db credentials just reduces security by adding extra steps that might fail.

5
  • I would still be concerned that tenant_id gets somehow stored in memory and used in a subsequent request. If a developer constructs a repository with tenant_id as argument and that is changed to a singleton later in time this would already cause a breach. Of course there could be governance on not to do such things but this is hard to enforce. Because of the many angles I could see a breach like that happening I would like to have each request handler executed by some kind of worker that can be destroyed when done to avoid any state being persisted between requests.
    – matteok
    Feb 1, 2022 at 12:01
  • That scenario would fail simple tests and also not make much sense architecturally. Its having the different connection per tenant which forces you into these caching scenarios in the first place. Remove that, and although of course you can construct a complex error with the same effect its hard to see why you would program that way
    – Ewan
    Feb 1, 2022 at 12:25
  • I can see that passing the tenant_id to the queries would reduce the likelihood of such a leak but if memory state is persisted between requests there are still many scenarios where this could happen. Given the fact that such a leak could be destructive to the business we would like to avoid persisting any memory state between requests.
    – matteok
    Feb 3, 2022 at 11:15
  • if you take the naïve approach, ie just call the db with the tenant id and return the results. nothing tenant specific is persisted at all. its stateless baring the db connection, which is tenantless
    – Ewan
    Feb 3, 2022 at 13:05
  • adding a tenant specific connection or handler, introduces the very problem you are trying to avoid
    – Ewan
    Feb 3, 2022 at 13:06

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.