防范恶意数据更改的策略

时间:2012-11-28 00:29:42

标签: breeze

寻找防止恶意数据更改的想法:userA操纵(编辑或删除)属于userB的数据。由于我们在客户端上创建实体,因此我们需要将它们(或至少其中一些)分配给经过身份验证的用户。

例如:

var newItem = ds.createNewItem();
newItem.OwnerId(22); //this is the problem that I see.    
newItem.Name("New Item");
newItem.Description("I just changed your item!");
... //and so on
ds.saveChanges();

假设我们知道在我们的API上调用SaveChanges的用户的身份,我们如何针对该用户验证我们的实体(新的或修改的)?

首先想到的是子类EFContextProvider,覆盖BeforeSaveEntity并根据用户的身份检查实体OwnerId属性。例如:

if (entityInfo.Entity.GetType() == typeof(Item)
    && (entityInfo.EntityState == EntityState.Added 
    || entityInfo.EntityState == EntityState.Modified)
    && ((Item)entityInfo.Entity).OwnerId != _currentUserId) {
    return false
    ... //and so on

如果使用这种方法,在新_currentUserId类的构造函数中建立EFContextProvider是否有意义?

一个想法或者更好的方法来解决这个问题?

1 个答案:

答案 0 :(得分:5)

我认为你走在正确的轨道上。我自己一直在唠叨这一点,并走了很多路。

我们假设您已经处理了身份验证,并且可以使用IPrincipal。您也可以自定义IIdentity(称之为AppIdentity),您可以为已认证用户存储UserId

Web Api的基础ApiController类通过其IPrincipal属性提供环境User。我们将在您的自定义Breeze Web Api控制器中利用它,这可能是这样的:

[Authorize]
[JsonFormatter, ODataActionFilter]
public class BreezeApiController : ApiController
{
    private readonly AppContextProvider _context;

    public BreezeApiController() {
        // pass 'User' IPrincipal to the context ctor
        _context = new AppContextProvider(User);
    }

    ...

    // one of the Query action methods
    [HttpGet]
    public IQueryable<Foo> Foos() {
        return _context.Foos
    }

    ...

您的自定义EFContextProvider可能会这样开始:

public class AppContextProvider : EFContextProvider<AppDbContext>
{
    public AppContextProvider(IPrincipal user)
    {
        UserId = ((AppIdentity) user.Identity).UserId;
    }

    public int UserId { get; private set; }
    ...

现在您可能希望防止UserA看到UserB的实体。因此,您的自定义Foo可以相应地过滤,而不是允许每个EFContextProvider出门。

   public DbQuery Foos
   {
       get 
       { 
           // Here the 'Context' is your EF DbContext
           return (DbQuery) Context.Foos
               .Where(f => f.UserId == UserId); 
       }
   }

回顾一下控制器,我们发现它的Foos GET动作方法对于过滤器一无所知......应该如此。我们希望我们的控制器很轻松,并将业务逻辑移动到自定义EFContextProvider及其帮助程序。

最后,高度简化的通用BeforeSaveEntity可能如下所示:

private bool BeforeSaveEntity(EntityInfo info)
{
    var entity = info.Entity;
    if (info.EntityState == EntityState.Added)
    {
        entity.UserId = UserId;
        return true;
    }
    return UserId == entity.UserId || throwCannotSaveEntityForThisUser();
}

...

private bool throwCannotSaveEntityForThisUser()
{
    throw new SecurityException("Unauthorized user");
}

请注意,服务器上的自定义上下文提供程序负责设置已添加实体的UserId。无论如何,我们不会相信客户这样做。当然,它负责验证已修改和删除的实体的UserId

希望这会有所帮助。 请记住,这只是草图。真正的交易将更加复杂,并被重构为帮助者。