防范恶意数据更改的策略

use*_*640 3 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();
Run Code Online (Sandbox Code Playgroud)

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

首先想到的是根据用户的身份对实体属性进行子类化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
Run Code Online (Sandbox Code Playgroud)

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

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

War*_*ard 5

我认为你走在正确的轨道上.我自己一直在兜售它,并走了很多路.

假设您已经处理了身份验证,并且可以使用身份验证IPrincipal.您也可以自定义一个自定义IIdentity(称之为AppIdentity),您可以在其中存储UserId经过身份验证的用户.

Web Api的基ApiControllerIPrincipal通过其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); 
       }
   }

回头看一下控制器,我们发现它的FoosGET动作方法对于过滤器一无所知......应该如此.我们希望我们的控制器很轻松,并将业务逻辑移动到自定义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修改和已删除的实体.

希望这可以帮助.请记住,这只是一个草图.真正的交易将更加复杂,并被重构为帮助者.