MultiTenant Application Prevent Tenant Access Data from Other Tenant in Shared Database RRS feed

  • Question

  • User-1011572351 posted

    I’m working on a tenant application and i was wondering how i can block tenant access other tenant data.

    First, let me expose some facts:

    The app is not free, 100% for sure the malicious user is a client.
    All the primary keys/identity are integers (Guid solve this problem but we can't change right now).
    The app use shared database and shared schema.
    All the tenants are business group wich own several shops.
    I'm use Forgery...
    I have some remote data chosen by dropdown and its easy change the id's and acess data from other tenants, if you have a little knowledge you can f*ck other tenants data.

    The first thing i think was check every remote field but this is kind annoying...

    So i build a solution compatible with Code First Migrations using Model Convention and Composite Keys, few tested, working as expected.

    Here's the "solution":

    Convention Class

    public class TenantSharedDatabaseSharedSchemaConvention<T> : Convention where T : class
        public Expression<Func<T, object>> PrimaryKey { get; private set; }
        public Expression<Func<T, object>> TenantKey { get; private set; }
        public TenantSharedDatabaseSharedSchemaConvention(Expression<Func<T, object>> primaryKey, Expression<Func<T, object>> tenantKey)
            this.PrimaryKey = primaryKey;
            this.TenantKey = tenantKey;
            base.Types<T>().Configure(m =>
                var indexName = string.Format("IX_{0}_{1}", "Id", "CompanyId");
                m.Property(this.PrimaryKey).IsKey().HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity).HasColumnOrder(0).HasColumnAnnotation("Index", new IndexAnnotation(new[] {
                    new IndexAttribute(indexName, 0) { IsUnique = true }
                m.Property(this.TenantKey).IsKey().HasDatabaseGeneratedOption(DatabaseGeneratedOption.None).HasColumnOrder(1).HasColumnAnnotation("Index", new IndexAnnotation(new[] {
                    new IndexAttribute(indexName, 1) { IsUnique = true }

    Convetion Registration:

    ** On convention register i pass two properties, first the primary key and second is the tenant id.

    modelBuilder.Conventions.Add(new TenantSharedDatabaseSharedSchemaConvention<BaseEntity>(m => m.Id, m => m.CompanyId));

    Base Entity Model

    public class BaseEntity
        public int Id { get; set; }
        public int CompanyId { get; set; }
        public Company Company { get; set; }

    Order Entity (Example)

    ** Here i reference the currency and client with company and all work as expected...

    public class Order : BaseEntity
        public int CurrencyId { get; set; }
        [ForeignKey("CompanyId, CurrencyId")]
        public virtual Currency Currency { get; set; }
        public int ClientId { get; set; }
        [ForeignKey("CompanyId, ClientId")]
        public virtual Client Client { get; set; }
        public string Description { get; set; }
    1. Is there any impact on performance?
    2. Is there any disadvantage compared to check every remote field?
    3. Someone have the same idea and/or problem and came with another solution? without composite ?



    Thursday, June 11, 2015 9:09 PM


  • User-84896714 posted

    Hi Rodrigo C,

    Thank you for your post.

    Is there any impact on performance?

    Of course yes.

    Is there any disadvantage compared to check every remote field?

    Check every remote field could solve the problem in essence, there also contains security problem in your workaround.

    Someone have the same idea and/or problem and came with another solution? without composite ?

    Every remote resource should has it's owner, only the owner is allowed to access the resource.

    Best Regards,

    Wang Li

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Monday, June 15, 2015 2:53 AM