如何引发反序列化异常? [英] How to throw a deserialized exception?

查看:88
本文介绍了如何引发反序列化异常?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用 JsonConvert.SerializeObject 在服务器上序列化 Exception ,然后编码为 byte [] 并在客户端中反序列化使用 JsonConvert.DeserializeObject .到目前为止一切正常...问题是当我抛出 Exception 被替换的堆栈跟踪时,让我演示一下:

  public void HandleException(RpcException exp){//获取异常字节[]字符串exceptionString = exp.Trailer.GetBytes("exception-bin");//反序列化异常异常exception = JsonConvert.DeserializeObject< Exception>(exceptionString,newJsonSerializerSettings {TypeNameHandling = TypeNameHandling.All});//记录异常:stacktrace是正确的.例如:在ServerMethod()Console.WriteLine(exception);//抛出相同的异常:更改了堆栈跟踪.例如:在HandleException()ExceptionDispatchInfo.Capture(exception).Throw();} 

解决方案

如果反序列化 Exception 并设置="https://docs.microsoft.com/zh-cn/dotnet/api/system.exception.stacktrace?view=net-5.0" rel ="nofollow noreferrer"> StackTrace 即使在引发异常之后:

  var设置=新的JsonSerializerSettings{TypeNameHandling = TypeNameHandling.All,上下文=新的StreamingContext(StreamingContextStates.CrossAppDomain),};var exception = JsonConvert.DeserializeObject< Exception>(exceptionString,设置); 

注意:

  • 之所以可行,是因为在用于 Exception ,反序列化的堆栈跟踪字符串将保存到 _remoteStackTraceString 中,该前缀随后会添加到常规堆栈跟踪中:

      if(context.State == StreamingContextStates.CrossAppDomain){//...可能会引发此新异常.从逻辑上来说,这是一次重新抛出,但是//实际上是一个全新的例外.由于清除了堆栈跟踪//在新的例外情况下,"_ remoteStackTraceString"提供给//有效地从远程"目录导入堆栈跟踪例外.所以,//将_stackTraceString移到_remoteStackTraceString中.笔记//如果已有_remoteStackTraceString,它将为//保留在新字符串的开头,因此所有内容都可以// 预期的.//即使未引发此异常,事情仍将按预期运行//因为StackTrace属性返回的是//_remoteStackTraceString和_stackTraceString._remoteStackTraceString = _remoteStackTraceString + _stackTraceString;_stackTraceString = null;} 

  • 虽然 Exception 的序列化流确实包含堆栈跟踪字符串,但它不会尝试捕获 ExceptionDispatchInfo > 在引发异常时无法复制和使用此信息.因此,似乎不可能抛出反序列化的异常并恢复其真实"异常.序列化流中的堆栈跟踪.

  • 为了使Json.NET使用其流构造函数反序列化类型(从而根据需要设置远程跟踪字符串),必须使用在Newtonsoft.Json 中反序列化自定义异常./p>

  • 使用 TypeNameHandling.All 反序列化异常是不安全的,并且在从不受信任的来源反序列化时可能导致注入攻击类型.请参阅: 由于Json.Net TypeNameHandling auto,外部JSON容易受到攻击? 其答案专门讨论了反序列化例外.

演示小提琴此处.

I'm serializing a Exception at the server with JsonConvert.SerializeObject and then encoding to a byte[] and deserializing in the client using JsonConvert.DeserializeObject. Everything works fine so far... The problem is when I throw the Exception the stacktrace being replaced, let me demostrate:

public void HandleException(RpcException exp)
{
    // Get the exception byte[]
    string exceptionString = exp.Trailer.GetBytes("exception-bin");
    
    // Deserialize the exception
    Exception exception = JsonConvert.DeserializeObject<Exception>(exceptionString, new 
    JsonSerializerSettings { TypeNameHandling = TypeNameHandling.All });
    
    // Log the Exception: The stacktrace is correct. Ex.: at ServerMethod()
    Console.WriteLine(exception);
    
    // Throw the same Exception: The stacktrace is changed. Ex.: at HandleException()
    ExceptionDispatchInfo.Capture(exception).Throw();
}

解决方案

If you deserialize an Exception and set JsonSerializerSettings.Context = new StreamingContext(StreamingContextStates.CrossAppDomain) then the deserialized stack trace string will get prepended to the displayed StackTrace even after the exception is thrown:

var settings = new JsonSerializerSettings
{
    TypeNameHandling = TypeNameHandling.All,
    Context = new StreamingContext(StreamingContextStates.CrossAppDomain),
};
var exception = JsonConvert.DeserializeObject<Exception>(exceptionString, settings);

Notes:

  • This works because, in the streaming constructor for Exception, the deserialized stack trace string is saved into a _remoteStackTraceString which is later prepended to the regular stack trace:

    if (context.State == StreamingContextStates.CrossAppDomain)
    {
        // ...this new exception may get thrown.  It is logically a re-throw, but 
        //  physically a brand-new exception.  Since the stack trace is cleared 
        //  on a new exception, the "_remoteStackTraceString" is provided to 
        //  effectively import a stack trace from a "remote" exception.  So,
        //  move the _stackTraceString into the _remoteStackTraceString.  Note
        //  that if there is an existing _remoteStackTraceString, it will be 
        //  preserved at the head of the new string, so everything works as 
        //  expected.
        // Even if this exception is NOT thrown, things will still work as expected
        //  because the StackTrace property returns the concatenation of the
        //  _remoteStackTraceString and the _stackTraceString.
        _remoteStackTraceString = _remoteStackTraceString + _stackTraceString;
        _stackTraceString = null;
    }
    

  • While the serialization stream for Exception does contain the stack trace string, it does not attempt to capture the private Object _stackTrace which is used by the runtime to identify where in the executing assembly the exception was thrown. This would seem to be why ExceptionDispatchInfo is unable to copy and use this information when throwing the exception. Thus it seems to be impossible to throw a deserialized exception and restore its "real" stack trace from the serialization stream.

  • In order Json.NET to deserialize a type using its streaming constructor (and thus set the remote trace string as required), the type must be marked with [Serializable] and implement ISerializable. System.Exception meets both requirements, but some derived classes of Exception do not always add the [Serializable] attribute. If your specific serialized exception lacks the attribute, see Deserializing custom exceptions in Newtonsoft.Json.

  • Deserializing an exception with TypeNameHandling.All is insecure and may lead to injection of attack types when deserializing from untrusted sources. See: External json vulnerable because of Json.Net TypeNameHandling auto? whose answer specifically discusses deserialization of exceptions.

Demo fiddle here.

这篇关于如何引发反序列化异常?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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