如何将SOAP反序列化失败的默认错误消息替换为自定义消息?
SOAP反序列化失败时替换自定义错误消息
当SOAP请求的数据成员反序列化失败时,服务默认返回的错误消息会暴露内部敏感信息(如类型名称、序列化细节),存在被攻击者利用的安全风险,需要将完整的fault string替换为自定义的通用消息。
请求示例
HTTP头
POST https://abc/xyz.svc HTTP/1.1 User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/109.0 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8 Accept-Language: en-US,en;q=0.5 Accept-Encoding: gzip, deflate Connection: close SOAPAction: http://abc/action Content-Type: text/xml;charset=UTF-8 Host: 127.0.0.1 Content-Length: 933
SOAP体
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:abc="http://action" xmlns:abc1="http://schemas.datacontract.org/2004/07/action.ServiceLibrary"> <soapenv:Header/> <soapenv:Body> <abc:Recon> <abc:employeeID>1231456</abc:employeeID> <abc:token>agthjknghrdfgbnhgevq</abc:token> <abc:request> <abc1:CurrencyCode><![CDATA[ <<!ENTITY % file SYSTEM "file:///etc/fstab"> <abc:Issuer></abc:Issuer> <abc:Reference>per auras</abc:Reference> </abc:request> </abc:Recon> </soapenv:Body> </soapenv:Envelope>
响应示例
HTTP/1.1 500 Internal Server Error Date: Tue, 23 May 2023 09:55:54 GMT Content-Type: text/xml; charset=utf-8 Content-Length: 703 Connection: close Access-Control-Expose-Headers: Request-Context Request-Context: appId=cid-v1:f2c7dfc0-df3b-4c74-a4d9-5fc744b61509 X-Content-Type-Options: nosniff X-Robots-Tag: noindex X-XSS-Protection: 1; mode=block <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"><s:Body><s:Fault><faultcode xmlns:a="http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher">a:DeserializationFailed</faultcode><faultstring xml:lang="en-US">The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter http://abc:request. The InnerException message was 'There was an error deserializing the object of type abc.ServiceLibrary.Request. The token '[CDATA[' was expected but found 'ENTITY '. Line 14, position 3.'. Please see InnerException for more your text details.</faultstring></s:Fault></s:Body></s:Envelope>
解决方案(针对WCF服务)
从响应的错误码命名空间可判断这是WCF服务,通过以下步骤替换自定义错误消息:
实现自定义错误处理器
创建实现IErrorHandler接口的类,捕获反序列化异常并替换为自定义消息:using System; using System.ServiceModel; using System.ServiceModel.Channels; using System.Runtime.Serialization; public class CustomDeserializationErrorHandler : IErrorHandler { // 标记异常已处理 public bool HandleError(Exception error) { return error is SerializationException || error.InnerException is SerializationException; } // 生成自定义Fault消息 public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { if (error is SerializationException || error.InnerException is SerializationException) { var faultReason = new FaultReason("请求格式无效,请检查请求参数后重试"); var faultCode = FaultCode.CreateSenderFaultCode( "DeserializationFailed", "http://schemas.microsoft.com/net/2005/12/windowscommunicationfoundation/dispatcher" ); fault = Message.CreateMessage(version, faultCode, faultReason, null); } } }创建自定义服务行为
实现IServiceBehavior接口,将错误处理器注入到服务运行时:using System; using System.ServiceModel; using System.ServiceModel.Description; using System.Collections.ObjectModel; public class CustomErrorBehavior : IServiceBehavior { public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers) { foreach (EndpointDispatcher endpointDispatcher in channelDispatcher.Endpoints) { endpointDispatcher.DispatchRuntime.ErrorHandlers.Add(new CustomDeserializationErrorHandler()); } } } // 空实现其他接口方法 public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } } // 定义行为特性,方便代码中直接标注 [AttributeUsage(AttributeTargets.Class)] public class CustomErrorBehaviorAttribute : Attribute, IServiceBehavior { public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { new CustomErrorBehavior().ApplyDispatchBehavior(serviceDescription, serviceHostBase); } public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } }启用自定义行为
- 代码方式:在服务类上添加特性
[CustomErrorBehavior] public class YourService : IYourService { // 服务实现代码 } - 配置文件方式:注册行为扩展并应用到服务
<system.serviceModel> <extensions> <behaviorExtensions> <add name="customErrorBehavior" type="YourNamespace.CustomErrorBehavior, YourAssemblyName"/> </behaviorExtensions> </extensions> <behaviors> <serviceBehaviors> <behavior name="ServiceWithCustomError"> <serviceDebug includeExceptionDetailInFaults="false"/> <customErrorBehavior/> </behavior> </serviceBehaviors> </behaviors> <services> <service name="YourNamespace.YourService" behaviorConfiguration="ServiceWithCustomError"> <endpoint address="" binding="basicHttpBinding" contract="YourNamespace.IYourService"/> </service> </services> </system.serviceModel>
- 代码方式:在服务类上添加特性
注意:确保serviceDebug的includeExceptionDetailInFaults设置为false,避免暴露内部异常细节。
内容的提问来源于stack exchange,提问作者user2361697
相关产品推荐
相关产品推荐

