C# ASP.NET MVC Core 2中使用MetadataPropertyHandling对JSON数据进行模型绑定
我已经为此挣扎了一段时间,我正在尝试将一个客户端JSON帖子绑定到ASP.NET Core 2 MVC中的一个模型,但我无法获取要获取的值。下面是我发布的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;
{
"$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
从未构造过
注:
- 通过使用
可以避免JSON中包含非多态属性的类型信息TypeNameHandling.Auto
- 在开发过程中,通过测试序列化预期结果
,可以确定文章正文中JSON的正确格式ModelBaseCollectionDTO
- 有关攻击失败原因的解释,请参阅。只要没有攻击小工具与基本模型类型兼容(可分配),您就应该是安全的
- 因为您在启动时没有设置
,所以您不会使您的其他API容易受到攻击,也不会使您自己暴露在通过所提到的深度嵌套多态属性进行攻击的情况下typenameholling
- 然而,为了增加安全性,您可能仍然需要创建自定义
ISerializationBinder