我在一个具有3个主要安全角色的项目中使用Breeze.js,AngularJS,Web API和EF6。让我们说高级,中级和低级。在这些示例中,我有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,它们没有在控制器中列出 - 我在breeze查询客户端使用expand来将它们拉进去。我担心的是任何人都可以编写自己的客户端代码和使用breeze的expand函数首先查询允许访问的实体,然后将其扩展到他们无法访问的其他实体。
我可以覆盖每个控制器操作的扩展深度:
[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方法并检查更改以确保它们对于用户角色是可接受的?是确保安全性以手动检查每个中级或高级属性的更改并比较用户角色的唯一方法吗?是否有一个良好的可扩展模式,可用于在大量实体上执行这些检查和其他业务逻辑。我找不到任何关于如何在更大的项目场景中正确实施这些业务规则的真实世界示例。
感谢您的所有帮助和建议,
答案 0 :(得分:0)
这是一个相当有争议的问题,因为您试图通过使用IQueryables等查询来限制公开敏感信息。这是一个可怕的设计缺陷,你可能会遇到很多麻烦。
不要将您的应用程序设计为可能根据未受到良好安全保护的客户端角色公开信息。只是不要这样做。在IQueryable中公开数据之前,您需要限制IQueryable在业务逻辑中有权访问的数据。
也不要依赖保存来保持正确。决不。这是一个非常糟糕的设计,我希望您在将其投入生产之前寻求某人的专业帮助!
有关详细信息,请参阅此问题/答案 - How is breeze.js handling security and avoiding exposing business logic
答案 1 :(得分:0)
好问题......好问题(复数)!我的任务是实施基于角色的安全性。昨天我实施了基于角色的安全保存。事实上,我确实以你所描述的方式实现了对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会话。我不太清楚这是怎么回事。