This article is available first on The New Stack - read it here.
Multi-tenant applications allow several distinct groups of users to access a system. Most SaaS solutions use multi-tenancy so that multiple organizations can sign up. Each customer becomes a "tenant" in the system with their own set of user accounts.
While multi-tenancy is often the most effective way to build new software, it poses challenges around authorization and privacy. You need to maintain strong boundaries between your tenants so customers can't access each other's data. In many cases, it's also desirable for support teams to have the ability to override those guard rails so they can diagnose problems experienced by individual tenants.
In this article, you'll learn about the authorization problems that multi-tenancy creates. It's important to understand these before you start building your system, so you can anticipate potential weaknesses and build adequate protections to address them.
Multi-tenancy is a computing model that allows many independent customers to use a single instance of a service. Customer One might have users A, B and C, while Customer Two has users X, Y and Z. All six users share the same software deployment, but users associated with Customer One shouldn't be able to access data created by users associated with Customer Two, or vice versa.
Service operators use multi-tenancy because it's more scalable, maintainable and cost-effective than single-tenant alternatives. Single-tenancy makes you provide each customer with a dedicated instance of your service, requiring management of more compute resources, some of which might sit idle when tenants are inactive. It also means that updates and rollbacks need to be applied across all of your instances, which increases the burden on operations teams.
Multi-tenancy can have a positive impact on security, too. It gives you a single attack surface that you can more easily monitor and control. When implemented correctly, multi-tenant systems provide robust logical isolation between customers, protecting privacy and data integrity.
Many of the risks of multi-tenancy originate from improper access control within your application. Safe multi-tenancy dependson an authorization model that reliably ties users to their data. Getting this right can be challenging if your system design doesn't anticipate where problems can occur.
Restricting users to the data that belongs to their tenant is the most fundamental requirement of multi-tenant authorization. Tenant isolation barriers are needed to prevent users from accessing sensitive information owned by another account. Such a breach would erode trust in your service and, depending on the type of exposure that occurred, could leave you liable to regulatory penalties.
Tenant identification usually occurs early in the life cycle of a request. Your service should authenticate the user, determine the tenant they belong to, then limit subsequent interactions to data that's associated with that tenant. Data fetches performed by your application need to be consistently constrained to avoid unintentional leakage of another tenant's information.
Issues often creep in when you implement endpoints that fetch an item specifically requested by the user. You could have an API such as /api/users/:userId
that provides the information associated with a user account. The requested user ID, such as /api/users/100
, might not be owned by the same tenant as the user who makes the interaction. You need a convenient mechanism for either constraining the database to fetch the active tenant's data, or checking whether a retrieved user record is accessible to the authenticated individual.
Software systems incorporate two different forms of authorization:
In the context of multi-tenancy, both these forms of authorization tend to overlap. Role-based authorization usually becomes implicitly dependent on resource-based authorization. Users who have a role (role-based authorization) can take the actions it provides, but only with respect to resources owned by their tenant (resource-based authorization). Continuing the example above, administrators can delete users, provided that the target user is part of their organization. The authentication routine first checks if the administrator has the “delete user” role, then ensures the target user is part of the same tenant.
This distinction can create subtleties in your authorization logic that don't surface in conventional single-tenant applications. Security policies always need to be conscious of the currently selected tenant, even if they're performing role-based authorization checks.
Another complication occurs when tenants require unique combinations of roles and actions to mirror their organization’s structures. One org might be satisfied by admin and read-only roles; another may need the admin role to be split into five distinct assignments. The most effective multi-tenant authorization systems will flexibly accommodate customizations on a per-tenant basis. At the application-level, granular permission checks will remain the same; however, the system will need to be configurable so tenants can create their own roles by combining different permissions.
It's usually fairly simple to implement basic tenant isolation, even if you're building your own authorization system. Things get more complicated once you develop a business requirement for cross-cutting between tenants. Support workers in SaaS organizations may exist in an internal tenant, but require access to neighboring tenants that belong to the service’s customers. Being able to view data from a user’s perspective is often the best way to effectively investigate and diagnose issues.
Cross-cutting has to be managed with care. It can make authorization much more difficult because it introduces new operating scenarios and the potential for edge case issues. You need to ensure only approved users can enter other tenants' spaces, and that their actions and activities in that space are restricted. Even in the context of a support request, it'll usually be inappropriate for a user's payment details to be exposed to an administrator, for example.
Authorization isn't the end of the story. Actions performed by users who are cutting across tenants need to be logged and monitored, so they can be audited in the future. This is vital, so you can determine who made a change if a user reports unrecognized activity on their account. Auditing may even be mandated for services that need to comply with specific regulatory frameworks. Logging can be incorporated into your authorization layer so that cross-cutting permission checks are recorded at the time they're made.
Accounts that are allowed to cross-cut should be viewed as high-risk assets and subjected to a stringent security regime, such as mandatory two-factor authentication. The privileges afforded by cross-cutting must also be kept to the bare minimum needed to accommodate each user's working responsibilities. Front-line staff should have less extensive access than senior support workers — it's safer to request an elevation if the first tier can't resolve the problem.
SaaS solutions often come with multiple subscription plans that allow customers to choose the right set of features for their needs. This requirement needs to be acknowledged within your authorization strategy. The authorization layer should be aware of the features available to the current tenant so it can deny roles that are associated with off-limits components.
Manually assigning and revoking roles as customers move between plans is one way to solve this, but it's a tedious and error-prone approach. Tying roles directly to the subscription features they're backed by is much more reliable. It also allows immediate resumption of previously assigned user capabilities if a customer downgrades, then later moves back to a higher tier.
Permission checks made by your application should first determine whether the tenant has the requisite feature, then look at whether the authenticated user has the requested role, and finally confirm that the target resource is owned by the tenant and passes any resource-specific authorization policies. Centralizing this logic using a policy-driven authorization platform is the best way to avoid mistakes as more variables affecting authorization outcomes are introduced.
Administrators, operators and engineering teams sometimes require elevated access that permits interactions with data from any tenant. This poses further risks and challenges that need to be considered before it's implemented.
Allowing root-like access to your service can streamline development tasks so that issues can be diagnosed more quickly. A developer who can log in, masquerade as the user who reported a bug and follow a set of reproduction steps is more likely to efficiently identify problems that only appear under specific circumstances.
This level of privilege must be carefully managed as you scale. In some systems, specific user accounts are granted the capability via hardcoded attributes such as their email address domain name or a special "administrator" flag. It's easy to lose track of these accounts as your organization grows, leading to a large number of over-privileged accounts.
Treating administrator access the same as any other role is a much safer prospect. Use your authorization provider to grant admins a role that includes all the actions known to your application, across every tenant. This lets you manage administrators using your existing authorization mechanisms. You'll be able to conveniently list all your root-level accounts and revoke their privileges when required, aiding compliance and monitoring.
Multi-tenancy is a scalable and cost-effective software delivery model in which multiple customers share an instance of your service. It's dependent on your application implementing proper authorization controls to isolate tenants and prevent users from moving laterally between them. Most systems will also need a mechanism that allows controlled override of those safeguards, so support teams and developers can effectively administer tenanted accounts.
Developing these authorization systems from scratch is a demanding task that can allow errors to occur. Fortunately, you can use an external provider to implement multi-tenancy in your application without having to reinvent all the safeguards.
Cerbos is an open source access control technology that you can self-host alongside the other components of your system. Cerbos lets you build your multi-tenant authorization rules as clear policies that are decoupled from your application's code. You can check whether a user's permitted to take a particular action using API calls or Cerbos' language-specific SDKs. Integrating with Cerbos accelerates development and reduces the risk of security holes, letting you concentrate on your system's unique functionality.
Book a free Policy Workshop to discuss your requirements and get your first policy written by the Cerbos team