我将类的一个实例序列化为一个文件(使用BinaryFormatter)
之后,在另一个项目中,我想反序列化这个文件,但是它没有工作,因为我的新项目没有我以前的类的描述。.Deserialize()得到一个异常
Unable to find assembly '*MyAssembly, Version=1.9.0.0, Culture=neutral, PublicKeyToken=null'.*".但是,我有程序集的.DLL,其中包含了我想反序列化的旧类的描述。
我不想在项目中添加引用这个DLL (我希望能够反序列化任何类型的程序集.)
如何通知序列化程序/反序列化程序使用动态加载的程序集?
发布于 2019-12-10 21:19:24
假设您是通过Assembly.Load()或Assembly.LoadFrom()加载程序集,然后按照这个答案 to https://stackoverflow.com/q/9162279/3744182 by 克里斯·肖恩中的解释,您可以在反序列化期间使用AppDomain.AssemblyResolve事件加载动态程序集。但是,出于安全原因,您将希望防止加载完全出乎意料的程序集。
一项可能的执行办法是采用以下措施:
public class AssemblyResolver
{
readonly string assemblyFullPath;
readonly AssemblyName assemblyName;
public AssemblyResolver(string assemblyName, string assemblyFullPath)
{
// You might want to validate here that assemblyPath really is an absolute not relative path.
// See e.g. https://stackoverflow.com/questions/5565029/check-if-full-path-given
this.assemblyFullPath = assemblyFullPath;
this.assemblyName = new AssemblyName(assemblyName);
}
public ResolveEventHandler AssemblyResolve
{
get
{
return (o, a) =>
{
var name = new AssemblyName(a.Name);
if (name.Name == assemblyName.Name) // Check only the name if you want to ignore version. Otherwise you can just check string equality.
return Assembly.LoadFrom(assemblyFullPath);
return null;
};
}
}
}然后,在启动的某个地方,向AppDomain.CurrentDomain.AssemblyResolve添加一个适当的AppDomain.CurrentDomain.AssemblyResolve,例如:
class Program
{
const string assemblyFullPath = @"C:\Full-path-to-my-assembly\MyAssembly.dll";
const string assemblyName = @"MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null";
static Program()
{
AppDomain.CurrentDomain.AssemblyResolve += new AssemblyResolver(assemblyName, assemblyFullPath).AssemblyResolve;
}此ResolveEventHandler检查请求的程序集是否具有动态程序集的名称,如果有,则从预期的完整路径加载当前版本。
另一种选择是编写自定义SerializationBinder并将其附加到BinaryFormatter.Binder。在BindToType (string assemblyName, string typeName)中,绑定程序需要检查属于动态程序集的类型,并将它们适当地绑定到它们。这里的诀窍是处理动态加载的类型嵌套在来自另一个程序集的泛型中的情况,例如List<MyClass>。在这种情况下,assemblyName将是List<T>而不是MyClass的程序集的名称。有关如何执行此操作的详细信息,请参阅
在评论 @sgnsajgon问询中,我想知道为什么我不能像在项目中显式引用签名程序集时那样反序列化流--只有formatter.Deserialize(stream)而没有其他东西。
虽然我不知道微软员工在设计这些类(回到.Net 1.1中)时在想什么,但这可能是因为:
BinaryFormatter安全性已经有点像垃圾箱火灾了,但是对BinaryFormatter流中任何意外的程序集名称自动调用Assembly.Load()可能会使事情变得更糟。
所谓“垃圾箱火”,我的意思是,开箱即用,BinaryFormatter将实例化并填充输入流中指定的类型,这些类型可能不是您所期望的类型。所以你可以这样做
var实例=(MyClass)新的BinaryFormatter().Deserialize(流);
但是,如果流实际上包含一个序列化的攻击小工具(如TempFileCollection ),那么将创建并填充该小工具,并进行攻击。
(有关这类攻击的详细信息,请参阅https://stackoverflow.com/q/39565954、https://stackoverflow.com/q/49038055、https://www.alphabot.com/security/blog/2017/net/How-to-configure-Json.NET-to-create-a-vulnerable-web-API.html和Alvaro Mu oz& Oleksandr Mirosh的黑帽纸https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf。这些链接指定如何修改Json.NET的配置以启用此类攻击;BinaryFormatter在默认配置中易受攻击。)
现在,如果BinaryFormatter以无法识别的程序集名称自动调用Assembly.Load(),那么使用它的应用程序可能还会受到DLL植入攻击的攻击,来自攻击DLL的攻击类型会意外地从某个意外位置加载,而不是从安全位置加载,从而进一步加剧攻击风险。
(顺便说一句,如果您选择编写自己的SerializationBinder,您可以筛选出意外的类型和/或已知的攻击类型,从而降低攻击小工具注入的风险。这也可能比预期的更难,因为BinaryFormatter流通常包括您可能不知道允许的序列化的私有或内部类。)顺便提一下,https://stackoverflow.com/q/703073/3744182对使用BinaryFormatter可能遇到的其他问题提供了有用的概述。
https://stackoverflow.com/questions/18881659
复制相似问题