Back to Blog
Architecture12 min read

Per-Tenant Fernet Encryption: Why Your Findings Are Unreadable to the Other 999 Tenants

P

Pentestas Team

Security Analyst

5/5/2026
Per-Tenant Fernet Encryption: Why Your Findings Are Unreadable to the Other 999 Tenants
TL;DR · Key insight

Explore how Pentestas implements per-tenant Fernet encryption to ensure that a single tenant's data remains secure and unreadable to others. This post delves into the architecture and techniques used to achieve robust tenant isolation in a multi-tenant environment.

Introduction to Tenant Isolation

In a multi-tenant SaaS environment, tenant isolation is a fundamental principle ensuring that each client's data remains separate and secure from others. This segregation is crucial because it prevents one tenant's data from being accessed by another, thereby upholding privacy and confidentiality. Imagine a scenario where data from one user could inadvertently be accessed by another; this would compromise the service's integrity and could lead to significant data breaches. Therefore, strict isolation protocols are not just a best practice but a necessity in creating a trustworthy SaaS offering.

The importance of tenant isolation extends beyond just separation; it involves securing data effectively. Encryption plays a vital role in this endeavor, ensuring that even if data is intercepted, it remains unreadable without the correct decryption key. In the context of SaaS, encryption safeguards the privacy of tenant data by converting it into a format that can only be deciphered by authorized parties. This makes encryption indispensable in protecting sensitive information, especially in environments with multiple tenants.

Implementing tenant isolation poses several challenges, such as managing encryption keys, ensuring consistent access control, and achieving performance efficiency. With each tenant requiring unique security measures, the complexity of maintaining a secure environment grows exponentially. To address these challenges, many SaaS providers turn to robust encryption methods like Fernet encryption. Fernet provides strong cryptographic guarantees, ensuring that data cannot be read or altered without detection. Additionally, Fernet is designed to be simple to use and implement, allowing for seamless integration into existing systems.

from cryptography.fernet import Fernet

# Generate a key for encryption
key = Fernet.generate_key()

# Create a Fernet instance with the encryption key
cipher = Fernet(key)

# Encrypting tenant data
tenant_data = b"Sensitive tenant data"
encrypted_data = cipher.encrypt(tenant_data)

# Decrypting tenant data
decrypted_data = cipher.decrypt(encrypted_data)

print(decrypted_data.decode())

Fernet encryption stands out due to its ability to handle symmetric encryption where the same key is used for both encryption and decryption. This makes it particularly efficient for environments with many tenants, as it simplifies key management. By employing Fernet, SaaS providers can ensure that each tenant's data is encrypted separately, maintaining the integrity and confidentiality of their information. Furthermore, Fernet's use of URL-safe base64 encoding ensures that the encrypted data can be easily stored and transmitted, even in environments with strict data handling requirements.

Understanding Fernet Encryption

Fernet encryption is a symmetric encryption method, which means it uses the same key for both encryption and decryption. This makes it essential for the key to be kept secure. The Fernet keys are generated using the cryptography.fernet.Fernet.generate_key() function, which produces a URL-safe base64-encoded 32-byte key. This key is crucial for ensuring that encrypted data remains confidential and is only accessible to those who possess the corresponding key.

Fernet encryption ensures data integrity by embedding a Message Authentication Code (MAC) within the encrypted payload. This MAC is used to verify that the encrypted data has not been tampered with. The combination of encryption and a MAC provides a robust layer of security, guaranteeing both confidentiality and authenticity. In practice, this means that any alteration in the ciphertext will render it unreadable, alerting us to potential tampering attempts.

from cryptography.fernet import Fernet

# Generate a Fernet key
key = Fernet.generate_key()

# Create a Fernet object
cipher_suite = Fernet(key)

# Encrypt data
cipher_text = cipher_suite.encrypt(b"Sensitive data")

# Decrypt data
plain_text = cipher_suite.decrypt(cipher_text)

Compared to other encryption methods, Fernet offers simplicity and ease of use, especially for developers who need to quickly implement secure encryption without diving into the complexities of asymmetric cryptography. However, one downside is key management; losing the Fernet key means losing access to the data. Unlike asymmetric methods where a public key can be shared freely, Fernet requires stringent controls over the key's distribution and storage.

Fernet fits neatly into the larger cryptographic landscape as a reliable choice for encrypting sensitive data at rest. It is best suited for scenarios where symmetric encryption is preferred, such as securing API tokens or personal data in multi-tenant architectures. While it doesn't replace more complex cryptographic strategies like RSA for key exchange or ECDSA for digital signatures, it complements them by providing a straightforward solution for encrypting data with a shared secret.

Implementing Per-Tenant Keys

Using individual keys per tenant is a cornerstone of our encryption strategy at Pentestas. The primary rationale behind this approach is to compartmentalize security and ensure that a breach in one tenant's data does not compromise others. Each tenant is assigned a unique Fernet key, which is a symmetric key used for both encryption and decryption. This means that if a key is compromised, only the data associated with that key is at risk, significantly reducing the potential impact of an attack.

Generating and assigning keys to each tenant involves a straightforward process. We utilize Python's cryptography library to generate Fernet keys. The keys are then securely stored and associated with the tenant's metadata in our database. Here's a brief code snippet demonstrating key generation:

from cryptography.fernet import Fernet

def generate_key():
    return Fernet.generate_key()

# Generate a key for a new tenant
new_tenant_key = generate_key()
print(new_tenant_key)

Key management is crucial to maintaining both security and efficiency. We employ a strategy that includes periodic key rotation and secure key storage using a Hardware Security Module (HSM). This ensures that keys are not only safe from unauthorized access but also remain valid and effective over time. By incorporating automated key rotation, we can minimize downtime and reduce the risk of key exhaustion, which is essential for maintaining operational efficiency.

The implementation of per-tenant keys significantly enhances data privacy and security by ensuring that each tenant's data is isolated. This approach also aligns with compliance standards such as GDPR, which mandates strict data protection measures. However, scaling key management can be challenging as the number of tenants grows. Solutions such as leveraging cloud-based key management services and implementing hierarchical key management structures can help address these scaling issues, ensuring that security measures remain robust as our infrastructure expands.

KMS Integration for Key Management

Key Management Systems (KMS) are essential for managing cryptographic keys in a secure and scalable manner. These systems provide a centralized solution for key storage, ensuring that sensitive keys are protected from unauthorized access. At Pentestas, we leverage KMS to securely store and retrieve encryption keys, facilitating the per-tenant encryption model that ensures data isolation across tenants. By integrating with a KMS, we can automate key generation and access, reducing the risk of human error and bolstering our security posture.

Our integration with KMS allows us to store keys securely and retrieve them on-demand. This is achieved by interfacing with the KMS API, which supports operations such as key creation, deletion, and access control. Below is a sample code snippet demonstrating how we interact with a KMS to fetch a key using the AWS SDK:

import boto3

kms_client = boto3.client('kms')

def get_key(key_id):
    response = kms_client.describe_key(KeyId=key_id)
    return response['KeyMetadata']

Using a KMS offers numerous benefits including scalability, as it can handle thousands of key requests simultaneously, and enhanced security, as it uses hardware security modules (HSMs) to protect keys. Compliance is another advantage, as many KMS providers are certified to meet industry standards like FIPS 140-2, ensuring that our encryption practices adhere to regulatory requirements.

Key rotation is a critical aspect of key lifecycle management that we handle within the KMS. By scheduling automatic key rotations, we ensure that encryption keys are regularly updated, minimizing potential risks from key exposure. The KMS supports this by allowing us to define policies for key rotation and lifecycle management, ensuring that our encryption solutions remain robust and secure over time.

Role-Based Access Control (RLS) in PostgreSQL

Role-Based Access Control (RLS) in PostgreSQL is a pivotal feature for enhancing database security. By allowing fine-grained access control, RLS ensures that only authorized users can view or manipulate data. This is particularly crucial in multi-tenant applications where sensitive data needs to be isolated between tenants. Without RLS, developers might resort to application-level checks, which are less secure and harder to maintain. RLS allows security policies to be defined directly on tables, ensuring that data access rules are enforced at the database level.

Implementing RLS in PostgreSQL involves setting policies that define which rows a user can access based on their role. This is achieved by creating policies with SQL commands like CREATE POLICY. For tenant isolation, a common approach is to add a tenant ID column to tables and create an RLS policy that filters rows based on the tenant ID. This ensures that users can only interact with data belonging to their tenant, providing a robust layer of security.

CREATE POLICY tenant_isolation_policy
  ON sensitive_data
  USING (tenant_id = current_setting('app.current_tenant_id')::int);
ENABLE ROW LEVEL SECURITY;

RLS complements encryption by ensuring that even if data is decrypted, it remains inaccessible to unauthorized users. While encryption protects data at rest and in transit, RLS manages access at the point of query execution. At Pentestas, we have implemented RLS policies such as SELECT and INSERT policies that enforce tenant isolation while allowing necessary operations.

Lessons Learned and Best Practices

Implementing RLS requires careful planning and testing to ensure it aligns with business logic and performance requirements. Regular audits and updates of RLS policies are essential to maintain security as application requirements evolve. It's also vital to educate developers and database administrators about the nuances of RLS to prevent accidental exposure of data.

Defense in Depth Approach

Implementing a defense in depth strategy means layering various security measures to protect data and systems, much like a castle with multiple rings of defense. This approach ensures that if one layer is breached, others remain intact to prevent unauthorized access. At Pentestas, we leverage this strategy to bolster our SaaS platform's security posture, ensuring that even if one control fails, others will mitigate the impact. This methodology is critical in environments where data protection is paramount, such as multi-tenant architectures where sensitive information from numerous clients is stored.

Our implementation combines encryption, Key Management Services (KMS), and Row-Level Security (RLS). Encryption ensures data is unreadable without proper decryption keys, KMS securely manages and rotates these keys, and RLS restricts data access at the database level based on user credentials. This combination forms a robust security framework, protecting data at rest and in transit. For instance, by using AWS KMS, we can programmatically encrypt database fields, ensuring each tenant's data is isolated. This layered security not only safeguards data but also aligns with compliance standards, such as GDPR and CCPA.

-- Example SQL for implementing RLS
CREATE POLICY tenant_isolation_policy
ON tenant_data
USING (tenant_id = current_setting('app.current_tenant_id')::int);

The benefits of a multi-layered security approach in SaaS are manifold. It not only enhances the resilience of systems against attacks but also provides peace of mind to our clients by ensuring their data is continually protected. A case study of Pentestas reveals that by utilizing this strategy, we successfully mitigated a potential breach where a compromised API key was unable to access any data due to additional RLS restrictions. This incident underscores the importance of not relying solely on a single security measure but rather an integrated suite of protections.

Future-Proofing Security

As threats evolve, so too must our security measures. By continuously updating and integrating new security technologies, Pentestas ensures that our platform remains resilient against emerging vulnerabilities. This proactive stance is crucial in a rapidly changing threat landscape.

Testing and Validating Tenant Isolation

Ensuring robust tenant isolation in a multi-tenant environment is critical, particularly when encryption mechanisms like Fernet are employed. At Pentestas, we prioritize thorough testing to confirm that encrypted data from one tenant remains unreadable to others. This requires a comprehensive approach to both encryption and access control testing. By simulating various attack vectors, we can identify potential vulnerabilities and reinforce our security stance. This process underscores the necessity of rigorous testing protocols to maintain the integrity and confidentiality of tenant-specific data.

To validate tenant isolation, we employ a suite of methods tailored to multi-tenant architectures. Our toolkit includes automated scripts and manual testing strategies that mimic real-world scenarios. For example, we might use Python scripts to generate Fernet keys for different tenants and attempt cross-tenant access. A typical script might look like this:

from cryptography.fernet import Fernet

tenant_a_key = Fernet.generate_key()
tenant_b_key = Fernet.generate_key()

fernet_a = Fernet(tenant_a_key)
fernet_b = Fernet(tenant_b_key)

data = b'sensitive data for tenant A'

encrypted_data = fernet_a.encrypt(data)

# Attempt to decrypt with tenant B's key should fail
try:
    fernet_b.decrypt(encrypted_data)
except Exception as e:
    print("Access denied: ", e)

Simulating a multi-tenant environment involves creating virtualized environments that replicate our client’s infrastructure. This allows us to test isolated access controls and encryption boundaries effectively. Continuous monitoring and auditing are integral to our security assurance process. We utilize tools like Splunk and ELK Stack to maintain real-time visibility into potential breaches or anomalies. Feedback loops are established to iterate on the testing process, allowing us to refine our approach based on test results and emerging threats.

Limitations and Future Directions

Despite the robustness of per-tenant encryption strategies, we face certain limitations. One significant challenge is the overhead associated with managing and rotating encryption keys for each tenant. As the number of tenants grows, the complexity of key management increases exponentially, potentially affecting performance. Additionally, the current Fernet encryption mechanism, while secure, lacks support for certain cryptographic algorithms that provide stronger security guarantees, such as AES-256-GCM.

from cryptography.fernet import Fernet

# Generate a key for a new tenant
tenant_key = Fernet.generate_key()

# Store the key securely
with open('/var/secure/tenant_key.key', 'wb') as key_file:
    key_file.write(tenant_key)

To mitigate these issues, we are exploring several avenues for improvement. Innovations in multi-party computation and homomorphic encryption offer promising solutions for enhancing data security without sacrificing performance. Furthermore, implementing a hierarchical key management system could streamline the process of key rotation and reduce overhead. As quantum computing becomes a more tangible threat, we are also investigating quantum-resistant algorithms to future-proof our encryption strategies against potential breakthroughs in quantum decryption.

Pentestas Security Roadmap

Our roadmap includes integrating advanced encryption techniques, enhancing key management processes, and preparing for quantum threats. We aim to balance rigorous security with optimal performance and ease of use.

Ultimately, our goal is to find the equilibrium between security, performance, and usability. While the pursuit of robust encryption is paramount, we must also consider the user experience, ensuring that security measures do not hinder the efficiency of our platform. As we continue to evolve our security architecture, these considerations will guide our development, ensuring that Pentestas remains at the forefront of secure, tenant-isolated data protection.

Try it on your stack

Free tier includes 10 scans/month on a verified domain. No credit card required.

Start scanning

Why this matters when buying pentesting-as-a-service

Pentestas is a pentesting-as-a-service offering — an AI penetration testing system that scans web apps, APIs, mobile binaries, cloud accounts, and internal networks under one platform. We default to penetration testing with Claude for triage and exploit-chain narration, and switch to penetration testing with DeepSeek for cost-sensitive bulk passes; both modes go through the same accuracy gate, the same destructive-payload guard, and the same reporting pipeline so a B2B SaaS pentest you run today and one you run six months from now produce comparable, auditable results.

If you've previously bought one-off engagements and you're comparing them against penetration testing with AI, the trade-offs in this post are the ones to read against your last consulting report.

Alexander Sverdlov

Alexander Sverdlov

Founder of Pentestas. Author of 2 information security books, cybersecurity speaker at the largest cybersecurity conferences in Asia and a United Nations conference panelist. Former Microsoft security consulting team member, external cybersecurity consultant at the Emirates Nuclear Energy Corporation.