使用ASP.NET MVC Core 2中的MetadataPropertyHandling模型绑定JSON数据 [英] Model binding JSON data with MetadataPropertyHandling in ASP.NET MVC Core 2

查看:264
本文介绍了使用ASP.NET MVC Core 2中的MetadataPropertyHandling模型绑定JSON数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经为此苦苦挣扎了一段时间,我正在尝试将客户端JSON帖子绑定到ASP.NET Core 2 MVC中的模型,但是我无法获取这些值.我要发布的JSON数据如下.

I've been struggling with this for a little while now, I'm trying to bind a client-side JSON post to a model in ASP.NET Core 2 MVC but I can't get the values to take. The JSON data I'm posting is below.

{
    "$type": "Namespace.ApplicationLoggingModel, Projectname.Web",
    "Test": "works"
}

...以及模型:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace Namespace.Models
{
    public class ApplicationLoggingModel
    {
        public string Test { get; set; }
    }
}

控制器动作似乎知道它是ApplicationLoggingModel类,但它不会绑定测试数据.我还尝试了下面的JSON,但这也不起作用.

The controller action seems to know it's ApplicationLoggingModel class, but it won't bind the test data. I've also tried the JSON below but that doesn't work either.

{
    "$Type": "Namespace.ApplicationLoggingModel, Projectname.Web",
    "$Values": [{
        "$Type": "System.String, mscorlib",
        "Test": true
    }]
}

我也尝试过"$ type"和"$ values",但是它似乎不区分大小写,所以我有点卡住了.在.NET Core和模型绑定中,这是否有可能?我不得不更改项目和名称空间的名称,因此,如果您需要更多信息,请告诉我.

I've also tried "$type" and "$values" but it doesn't appear to be case sensitive, so I'm a little bit stuck. Is this even possible in .NET Core and with model binding? I've had to change the project and namespace names so let me know if you need any more information.

更新:我已经在下面添加了控制器操作,尝试了常规模型绑定,因为自从添加[FromBody]标签以使常规模型绑定开始工作以来,$ type似乎不起作用.该模型现在为空.

UPDATE: I've added the controller action below, having tried regular model binding it seems like the $type isn't working as I've since added the [FromBody] tag to get the regular model binding to work. The model is now coming through null.

[HttpPost("Save/{id}")]
public ActionResult Save(Guid id, [FromBody]ApplicationLoggingModel model)
{
    return RedirectToAction("Entities", Guid.Empty);
}

正如dbc正确指出的那样,我的ApplicationLoggingModel不是多态的,因此不需要TypeNameHandling-ApplicationLoggingModel将包含一个多态模型的数组,我基本上已经在这里退后了一步,以尝试并实现此功能在其他模型上实现它之前.

as dbc rightly pointed out, my ApplicationLoggingModel isn't polymorphic and therefore doesn't need TypeNameHandling - ApplicationLoggingModel will contain an array of polymorphic Models, I've basically taken a step back here to try and get one this working before I can implement it on the other models.

推荐答案

As shown in How to configure Json.NET to create a vulnerable web API, you can enable TypeNameHandling during deserialization and model binding throughout your entire object graph by doing

services.AddMvc().AddJsonOptions(options =>
{
    options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;
});

Startup.cs中.

但是,这样做可能会引入该文章以及Newtonsoft Json中的 TypeNameHandling警告中所述的安全风险 a> .因此,除非您创建自定义 ISerializationBinder 过滤掉不必要或意外的类型.

However, doing this can introduce security risks as described in that very article as well as TypeNameHandling caution in Newtonsoft Json. As such I would not recommend this solution unless you create a custom ISerializationBinder to filter out unwanted or unexpected types.

作为此风险解决方案的替代方法,如果仅需要使根模型列表具有多态性,则可以使用以下方法:

As an alternative to this risky solution, if you only need to make your list of root models be polymorphic, the following approach can be used:

  1. 从应用程序中定义的某些常见基类或接口派生所有多态模型(即不是某些系统类型,例如 INotifyPropertyChanged ).

  1. Derive all of your polymorphic models from some common base class or interface defined in your application (i.e. not some system type such as CollectionBase or INotifyPropertyChanged).

使用单个属性List<T>类型定义容器 DTO ,其中T是您的常用基本类型.

Define a container DTO with single property of type List<T> where T is your common base type.

使用 [JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)] 标记该属性.

Mark that property with [JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)].

在启动时设置options.SerializerSettings.TypeNameHandling = TypeNameHandling.All;.

要了解其在实际中的工作原理,请说您具有以下模型类型层次结构:

To see how this works in practice, say you have the following model type hierarchy:

public abstract class ModelBase
{
}

public class ApplicationLoggingModel : ModelBase
{
    public string Test { get; set; }
}

public class AnotherModel : ModelBase
{
    public string AnotherTest { get; set; }
}

然后按如下所示定义您的根DTO:

Then define your root DTO as follows:

public class ModelBaseCollectionDTO
{
    [JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)]
    public List<ModelBase> Models { get; set; }
}

然后,如果您按照以下方式构造它的实例并序列化为JSON:

Then if you construct an instance of it as follows and serialize to JSON:

var dto = new ModelBaseCollectionDTO
{
    Models = new List<ModelBase>()
    {
        new ApplicationLoggingModel { Test = "test value" },
        new AnotherModel { AnotherTest = "another test value" },
    },
};

var json = JsonConvert.SerializeObject(dto, Formatting.Indented);

将生成以下JSON:

{
  "Models": [
    {
      "$type": "Namespace.Models.ApplicationLoggingModel, TestApp",
      "Test": "test value"
    },
    {
      "$type": "Namespace.Models.AnotherModel, TestApp",
      "AnotherTest": "another test value"
    }
  ]
}

然后可以将其反序列化为ModelBaseCollectionDTO,而不会丢失类型信息,而无需全局设置ItemTypeNameHandling:

This can then be deserialized back into a ModelBaseCollectionDTO without loss of type information and without needing to set ItemTypeNameHandling globally:

var dto2 = JsonConvert.DeserializeObject<ModelBaseCollectionDTO>(json);

示例工作小提琴.

但是,如果我尝试进行

However, if I attempt the attack shown in How to configure Json.NET to create a vulnerable web API as follows:

try
{
    File.WriteAllText("rce-test.txt", "");
    var badJson = JToken.FromObject(
        new
        {
            Models = new object[] 
            {
                new FileInfo("rce-test.txt") { IsReadOnly = false },
            }
        },
        JsonSerializer.CreateDefault(new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.Auto, Formatting = Formatting.Indented }));
    ((JObject)badJson["Models"][0])["IsReadOnly"] = true;
    Console.WriteLine("Attempting to deserialize attack JSON: ");
    Console.WriteLine(badJson);
    var dto2 = JsonConvert.DeserializeObject<ModelBaseCollectionDTO>(badJson.ToString());
    Assert.IsTrue(false, "should not come here");
}
catch (JsonException ex)
{
    Assert.IsTrue(!new FileInfo("rce-test.txt").IsReadOnly);
    Console.WriteLine("Caught expected {0}: {1}", ex.GetType(), ex.Message);
}

然后将文件rce-test.txt标记为 not 为只读,并引发以下异常:

Then the file rce-test.txt is not marked as read-only, and instead the following exception is thrown:

Newtonsoft.Json.JsonSerializationException:JSON'System.IO.FileInfo,mscorlib'中指定的类型与'Namespace.Models.ModelBase,Tile'不兼容.路径"Models [0].$ type",第4行,位置112.

Newtonsoft.Json.JsonSerializationException: Type specified in JSON 'System.IO.FileInfo, mscorlib' is not compatible with 'Namespace.Models.ModelBase, Tile'. Path 'Models[0].$type', line 4, position 112.

表明攻击小工具FileInfo从未被构建.

Indicating that the attack gadget FileInfo is never even constructed.

注意:

  • 使用TypeNameHandling.Auto可以避免使用非多态属性的类型信息使JSON膨胀.

  • By using TypeNameHandling.Auto you avoid bloating your JSON with type information for non-polymorphic properties.

在发布正文中JSON的正确格式可以通过在开发过程中对预期的结果ModelBaseCollectionDTO进行序列化测试来确定.

The correct format for the JSON in your post body can be determined by test-serializing the expected resulting ModelBaseCollectionDTO during development.

有关攻击失败原因的解释,请参见由于Json.Net TypeNameHandling自动而易受攻击的外部json? .只要没有攻击小工具与您的基本模型类型兼容(可分配),您就应该是安全的.

For an explanation of why the attack fails see External json vulnerable because of Json.Net TypeNameHandling auto?. As long as no attack gadget is compatible with (assignable to) your base model type, you should be secure.

因为您没有在启动时设置TypeNameHandling,所以不会使其他API容易受到攻击,也不会通过深度嵌套的多态属性(例如提到的

Because you do not set TypeNameHandling in startup you are not making your other APIs vulnerable to attack or exposing yourself to attacks via deeply nested polymorphic properties such as those mentioned here.

尽管如此,为了增加安全性,您可能仍想创建自定义的ISerializationBinder.

Nevertheless, for added safety, you may still want to create a custom ISerializationBinder.

这篇关于使用ASP.NET MVC Core 2中的MetadataPropertyHandling模型绑定JSON数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆