Безопасность на основе ролей в breezejs и EF6

Я использую Breeze.js, AngularJS, Web API и EF6 в проекте, который имеет 3 основных роли безопасности. Допустим, высокий уровень, средний уровень и низкий уровень. В этих примерах у меня есть сущности Person, Company, LowLevelSecret, MediumLevelSecret, HighLevelSecret.

Проблема безопасности 1: в первом примере я хочу иметь возможность защищать доступ к объектам в целом. Все роли безопасности (низкий уровень, средний уровень и высокий уровень) должны иметь возможность доступа к объектам Person. Только пользователи с соответствующим уровнем роли или выше должны иметь доступ к секретным объектам, которые содержат привилегированную информацию.

Например, я мог бы иметь.

class Person {
    public int Id { get; set; }
    public string Name { get; set; }

    public LowLevelSecret LowLevelSecret { get; set; }
    public MediumLevelSecret MediumLevelSecret { get; set; }
    public HighLevelSecret HighLevelSecret { get; set; }
}

Где LowLevelSecret, MediumLevelSecret и HighLevelSecret будут выглядеть примерно так:

class LowLevelSecret {
    public int Id { get; set; }
    public string Secret { get; set; }
}
class MediumLevelSecret {
    public int Id { get; set; }
    public string Secret { get; set; }
}
class HighLevelSecret {
    public int Id { get; set; }
    public string Secret { get; set; }
}

У меня есть контроллер, который IQueryable для типа для человека и компании:

class BreezeController : ApiController {
    [HttpGet]
    public string Metadata()
    {
        return _repository.Metadata;
    }
    [HttpPost]
    public SaveResult SaveChanges(JObject saveBundle)
    {
        var result = _repository.SaveChanges(saveBundle);
        return result;
    }
    [HttpGet]
    public IQueryable<Person> People()
    {
        return _repository.People;
    }
    [HttpGet]
    public IQueryable<Company> Companies()
    {
        return _repository.Companies;
    }
}

Я не могу использовать AuthorizeAttribute для секретных объектов, они не перечислены в контроллере - я использую раскрытие на стороне клиента легкого запроса, чтобы вытащить их. Меня беспокоит то, что любой может написать свой собственный код на стороне клиента и использовать расширение функция быстрого доступа к первому запросу объекта, к которому им разрешен доступ, а затем расширение его до других объектов, к которым у них не должно быть доступа.

Я мог бы переопределить глубину расширения для каждого действия контроллера:

[HttpGet]
[BreezeQueryable(MaxExpansionDepth = 0)]
public IQueryable<Person> People()
{
    return _repository.People;
}

Это кажется немного громоздким и подверженным ошибкам. Есть ли лучший способ защитить то, что отправляется клиенту?

Проблема безопасности 2 Скажем, у меня есть компания, как указано ниже:

public Company {
    // low level, medium level and high level access required
    public int Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }

    // medium level, high level access only - NOT low level
    public string DirectorsName { get; set; }
    public string DirectorsEmail { get; set; }

    // high level access only - NOT low level, medium level
    public string Profit { get; set; }
    public string Turnover { get; set; }
}

Как мне ограничить доступ, как описано, чтобы разные роли пользователей могли обращаться к разным частям одного и того же объекта Компании. Я мог бы написать проекционный запрос, чтобы получить только нужные биты, но ничто не мешает пользователю написать собственный запрос для всей сущности. Я предполагаю, что ключом здесь может быть использование разных DTO для каждого уровня доступа? Как это может работать, чтобы все это можно было объединить при сохранении и т. Д., Чтобы он знал, какой объект нужно обновить.

Проблема безопасности 3 Метод SaveChanges в контроллере принимает пакет изменений. Кажется, что нет ничего, что могло бы помешать пользователю с более низким уровнем доступа отправлять изменения в сущности, которые они не могли бы изменить. Даже если обычный клиентский код не позволяет этого, они могут просто написать свой собственный или создать его вручную и отправить.

Я считаю, что предпочтительным решением для этого является переопределение метода BeforeSavingEntity и проверка изменений, чтобы убедиться, что они приемлемы для роли пользователей? Является ли единственный способ обеспечить безопасность, чтобы вручную проверять изменения каждого свойства среднего или высокого уровня и сравнивать роль пользователей? Существует ли хороший масштабируемый шаблон, который можно использовать для выполнения этих проверок и другой бизнес-логики на большом количестве объектов. Я не могу найти хороших реальных примеров того, как правильно применять такие бизнес-правила в сценарии более крупного проекта.

Спасибо за вашу помощь и предложения,

2 ответа

Хороший вопрос... ну вопросы (во множественном числе)! Мне поручено реализовать безопасность на основе ролей. Вчера я реализовал ролевую защиту для сохранения. Фактически я реализовал проверку в BeforeSaveEntity() ... так, как вы описали. Это кажется дорогой операцией, но я не знаю лучшего способа.

Что ж, вы можете немного оптимизировать, переопределив BeforeSaveEntities().

Что касается того, как реализовать ролевую безопасность для запросов... Я все еще работаю над этим:) У меня есть пара идей. Но я пока не смог их реализовать, чтобы сказать, работают они или нет. Тем не менее, вот мои идеи:

1) очень похож на алгоритм сохранения, проверяйте сущности - после выполнения запроса, но до того, как результат будет отправлен клиенту. Но я не знаю, как вставить этот проверочный код (через обратный вызов или что-то еще) в момент между выполнением и отправкой клиенту:(

2) добавить смарты в POCO, чтобы проверить наличие ограниченных объектов и свойств в зависимости от роли пользователя. Например, вместо:

public string P {get;set;}

У вас будет что-то вроде этого:

private string _p;
public string P 
{ 
  get 
  { 
    if (UserRoles.HasAny("role-a","role-b"))
      return _p;
    return null; 
  }
  set { _p = value; }
}

Это кажется странным, так как POCO должен быть тупым. POCO должен был бы иметь возможность читать роли пользователя откуда-то... может быть, сеанс HTTP. Я не совсем знаю, как это будет работать.

Это довольно спорный вопрос, потому что вы пытаетесь ограничить раскрытие конфиденциальной информации, используя запросы с IQueryables и тому подобное. Это ужасный недостаток дизайна, и вы можете получить много неприятностей.

Не проектируйте свое приложение так, чтобы оно потенциально могло предоставлять информацию на основе ролей клиента, которые не защищены должным образом. Просто не делай этого. Прежде чем предоставлять данные в IQueryable, необходимо ограничить данные, к которым IQueryable имеет доступ, в вашей бизнес-логике.

Также НЕ полагайтесь на сохранение, чтобы всегда быть правильным. Никогда. Это очень плохой дизайн, и я надеюсь, что вы обратитесь за профессиональной помощью к кому-то, прежде чем приступить к работе!

Для получения дополнительной информации, пожалуйста, смотрите этот вопрос / ответ - Как breeze.js управляет безопасностью и избегает разоблачения бизнес-логики

Другие вопросы по тегам