Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/json/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# ASP.NET MVC Core 2中使用MetadataPropertyHandling对JSON数据进行模型绑定_C#_Json_Asp.net Core - Fatal编程技术网

C# ASP.NET MVC Core 2中使用MetadataPropertyHandling对JSON数据进行模型绑定

C# ASP.NET MVC Core 2中使用MetadataPropertyHandling对JSON数据进行模型绑定,c#,json,asp.net-core,C#,Json,Asp.net Core,我已经为此挣扎了一段时间,我正在尝试将一个客户端JSON帖子绑定到ASP.NET Core 2 MVC中的一个模型,但我无法获取要获取的值。下面是我发布的JSON数据 { "$type": "Namespace.ApplicationLoggingModel, Projectname.Web", "Test": "works" } …以及模型: using System; using System.Collections.Generic; using System.Linq;

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

{
    "$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,但也不起作用

{
    "$Type": "Namespace.ApplicationLoggingModel, Projectname.Web",
    "$Values": [{
        "$Type": "System.String, mscorlib",
        "Test": true
    }]
}
我也尝试过“$type”和“$values”,但它似乎不区分大小写,所以我有点卡住了。在.NETCore和模型绑定中也可以这样做吗?我必须更改项目和名称空间名称,因此如果需要更多信息,请告诉我

更新:我在下面添加了控制器操作,在尝试常规模型绑定之后,$type似乎不起作用,因为我添加了
[FromBody]
标记以使常规模型绑定起作用。该模型现在正在通过null

[HttpPost("Save/{id}")]
public ActionResult Save(Guid id, [FromBody]ApplicationLoggingModel model)
{
    return RedirectToAction("Entities", Guid.Empty);
}
编辑:正如dbc正确指出的,我的ApplicationLoggingModel不是多态的,因此不需要类型名处理-ApplicationLoggingModel将包含一系列多态模型,我基本上在这里后退了一步,在我可以在其他模型上实现它之前,尝试让它工作起来。

如中所示,通过执行以下操作,可以在整个对象图的反序列化和模型绑定期间启用

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

然而,这样做可能会带来安全风险,正如文章中所描述的那样。因此,除非您创建一个自定义来过滤掉不需要的或意外的类型,否则我不会推荐此解决方案

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

  • 从应用程序中定义的一些公共基类或接口派生所有多态模型(即,不是某些系统类型,例如或)

  • 使用
    List
    类型的单个属性定义一个容器,其中
    T
    是您常用的基本类型

  • 将该属性标记为

  • 不要设置
    options.SerializerSettings.TypeNameHandling=TypeNameHandling.All在启动中

  • 要了解这在实践中是如何工作的,假设您具有以下模型类型层次结构:

    public abstract class ModelBase
    {
    }
    
    public class ApplicationLoggingModel : ModelBase
    {
        public string Test { get; set; }
    }
    
    public class AnotherModel : ModelBase
    {
        public string AnotherTest { get; set; }
    }
    
    然后定义根DTO,如下所示:

    public class ModelBaseCollectionDTO
    {
        [JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)]
        public List<ModelBase> Models { get; set; }
    }
    
    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);
    }
    
    然后可以将其反序列化回
    ModelBaseCollectionDTO
    ,而不丢失类型信息,也不需要全局设置
    ItemTypeNameHandling

    var dto2 = JsonConvert.DeserializeObject<ModelBaseCollectionDTO>(json);
    
    var dto2=JsonConvert.DeserializeObject(json);
    
    样品加工

    但是,如果我尝试如下所示的攻击:

    public class ModelBaseCollectionDTO
    {
        [JsonProperty(ItemTypeNameHandling = TypeNameHandling.Auto)]
        public List<ModelBase> Models { get; set; }
    }
    
    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);
    }
    
    试试看
    {
    writealText(“rce test.txt”,即“”);
    var badJson=JToken.FromObject(
    新的
    {
    模型=新对象[]
    {
    新文件信息(“rce test.txt”){IsReadOnly=false},
    }
    },
    CreateDefault(新的JsonSerializerSettings{TypeNameHandling=TypeNameHandling.Auto,Formatting=Formatting.Indented});
    ((JObject)badJson[“Models”][0])[“IsReadOnly”]=true;
    WriteLine(“试图反序列化攻击JSON:”;
    Console.WriteLine(badJson);
    var dto2=JsonConvert.DeserializeObject(badJson.ToString());
    Assert.IsTrue(false,“不应该到这里来”);
    }
    捕获(JsonException ex)
    {
    Assert.IsTrue(!newfileinfo(“rce test.txt”).IsReadOnly);
    WriteLine(“捕获预期的{0}:{1}”,例如GetType(),例如Message);
    }
    
    然后文件
    rce test.txt
    未标记为只读,而是引发以下异常:

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

    表示攻击小工具
    FileInfo
    从未构造过

    注:

    • 通过使用
      TypeNameHandling.Auto
      可以避免JSON中包含非多态属性的类型信息

    • 在开发过程中,通过测试序列化预期结果
      ModelBaseCollectionDTO
      ,可以确定文章正文中JSON的正确格式

    • 有关攻击失败原因的解释,请参阅。只要没有攻击小工具与基本模型类型兼容(可分配),您就应该是安全的

    • 因为您在启动时没有设置
      typenameholling
      ,所以您不会使您的其他API容易受到攻击,也不会使您自己暴露在通过所提到的深度嵌套多态属性进行攻击的情况下

    • 然而,为了增加安全性,您可能仍然需要创建自定义
      ISerializationBinder


    既然您的<代码>应用程序登录模型
    不是多态的,为什么您需要它呢?这样做可能会带来安全风险,请参见和。它将包含一系列多态模型,我基本上在这里后退了一步,尝试让一个组件工作,然后才能在其他模型上实现它。我将编辑我的帖子来指出这一点。然后,奇怪的是,前面提到的内容似乎说明了如何使它起作用。在该示例中,有一个多态属性,而不是多态根模型。此外,您需要在启动期间配置选项、序列化设置、TypeNameHandling。顺便问一下,您的多态模型数组中的模型是否派生自某个公共基类或接口?它们派生自一个抽象类,我还不知道该部分是否工作,它是由另一个开发人员编写的。同样感谢您的文章,添加options.SerializerSettings.typenameholling=typenameholling.All to AddJsonOptions已经解决了这个问题。我得补充一下