安全框架:异常管理 | 缓解措施Security Frame: Exception Management | Mitigations

产品/服务Product/Service 文章Article
WCFWCF
Web APIWeb API
Web 应用程序Web Application

WCF - 不要在配置文件中包含 serviceDebug 节点WCF- Do not include serviceDebug node in configuration file

标题Title 详细信息Details
组件Component WCFWCF
SDL 阶段SDL Phase 构建Build
适用的技术Applicable Technologies 泛型、NET Framework 3Generic, NET Framework 3
属性Attributes 空值N/A
参考References MSDN巩固王国MSDN, Fortify Kingdom
步骤Steps 可将 Windows Communication Framework (WCF) 服务配置为公开调试信息。Windows Communication Framework (WCF) services can be configured to expose debugging information. 不应在生产环境中使用调试信息。Debug information should not be used in production environments. <serviceDebug> 标记定义是否为 WCF 服务启用调试信息功能。The <serviceDebug> tag defines whether the debug information feature is enabled for a WCF service. 如果 includeExceptionDetailInFaults 特性设置为 true,则将来自应用程序的异常信息返回到客户端。If the attribute includeExceptionDetailInFaults is set to true, exception information from the application will be returned to clients. 攻击者可以利用他们从调试输出中获取的附加信息,针对框架、数据库或者应用程序使用的其他资源展开攻击。Attackers can leverage the additional information they gain from debugging output to mount attacks targeted on the framework, database, or other resources used by the application.

示例Example

以下配置文件包含 <serviceDebug> 标记:The following configuration file includes the <serviceDebug> tag:

<configuration> 
<system.serviceModel> 
<behaviors> 
<serviceBehaviors> 
<behavior name=""MyServiceBehavior""> 
<serviceDebug includeExceptionDetailInFaults=""True"" httpHelpPageEnabled=""True""/> 
... 

在服务中禁用调试信息。Disable debugging information in the service. 可通过在应用程序的配置文件中删除 <serviceDebug> 标记来实现此目的。This can be accomplished by removing the <serviceDebug> tag from your application's configuration file.

WCF - 不要在配置文件中包含 serviceMetadata 节点WCF- Do not include serviceMetadata node in configuration file

标题Title 详细信息Details
组件Component WCFWCF
SDL 阶段SDL Phase 构建Build
适用的技术Applicable Technologies 泛型Generic
属性Attributes 泛型、NET Framework 3Generic, NET Framework 3
参考References MSDN巩固王国MSDN, Fortify Kingdom
步骤Steps 有关服务的公开信息可能会给攻击者提供有用的洞察数据,使他们知道如何攻击服务。Publicly exposing information about a service can provide attackers with valuable insight into how they might exploit the service. <serviceMetadata> 标记会启用元数据发布功能。The <serviceMetadata> tag enables the metadata publishing feature. 服务元数据可能包含不应该提供给公众访问的敏感信息。Service metadata could contain sensitive information that should not be publicly accessible. 最起码,应该只允许受信任的用户访问元数据,确保不必要的信息不会公开。At a minimum, only allow trusted users to access the metadata and ensure that unnecessary information is not exposed. 当然,更好的做法是完全禁用元数据发布功能。Better yet, entirely disable the ability to publish metadata. 安全的 WCF 配置不包含 <serviceMetadata> 标记。A safe WCF configuration will not contain the <serviceMetadata> tag.

确保在 ASP.NET Web API 中进行适当的异常处理Ensure that proper exception handling is done in ASP.NET Web API

标题Title 详细信息Details
组件Component Web APIWeb API
SDL 阶段SDL Phase 构建Build
适用的技术Applicable Technologies MVC 5、MVC 6MVC 5, MVC 6
属性Attributes 空值N/A
参考References ASP.NET Web API 中的异常处理ASP.NET Web API 中的模型验证Exception Handling in ASP.NET Web API, Model Validation in ASP.NET Web API
步骤Steps 默认情况下,ASP.NET Web API 中大多数未捕获的异常将转换成状态代码为 500, Internal Server Error 的 HTTP 响应By default, most uncaught exceptions in ASP.NET Web API are translated into an HTTP response with status code 500, Internal Server Error

示例Example

若要控制 API 返回的状态代码,可按如下所示使用 HttpResponseExceptionTo control the status code returned by the API, HttpResponseException can be used as shown below:

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        throw new HttpResponseException(HttpStatusCode.NotFound);
    }
    return item;
}

示例Example

若要进一步控制异常响应,可按如下所示使用 HttpResponseMessage 类:For further control on the exception response, the HttpResponseMessage class can be used as shown below:

public Product GetProduct(int id)
{
    Product item = repository.Get(id);
    if (item == null)
    {
        var resp = new HttpResponseMessage(HttpStatusCode.NotFound)
        {
            Content = new StringContent(string.Format("No product with ID = {0}", id)),
            ReasonPhrase = "Product ID Not Found"
        }
        throw new HttpResponseException(resp);
    }
    return item;
}

若要捕捉类型不是 HttpResponseException 的未经处理的异常,可以使用异常筛选器。To catch unhandled exceptions that are not of the type HttpResponseException, Exception Filters can be used. 异常筛选器实现 System.Web.Http.Filters.IExceptionFilter 接口。Exception filters implement the System.Web.Http.Filters.IExceptionFilter interface. 编写异常筛选器的最简单方法是从 System.Web.Http.Filters.ExceptionFilterAttribute 类派生,并重写 OnException 方法。The simplest way to write an exception filter is to derive from the System.Web.Http.Filters.ExceptionFilterAttribute class and override the OnException method.

示例Example

下面是可将 NotImplementedException 异常转换为 HTTP 状态代码 501, Not Implemented 的筛选器:Here is a filter that converts NotImplementedException exceptions into HTTP status code 501, Not Implemented:

namespace ProductStore.Filters
{
    using System;
    using System.Net;
    using System.Net.Http;
    using System.Web.Http.Filters;

    public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute 
    {
        public override void OnException(HttpActionExecutedContext context)
        {
            if (context.Exception is NotImplementedException)
            {
                context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented);
            }
        }
    }
}

可通过多种方法注册 Web API 异常筛选器:There are several ways to register a Web API exception filter:

  • 按操作By action
  • 按控制器By controller
  • 全局Globally

示例Example

要将筛选器应用到特定的操作,请将筛选器作为特性添加到该操作:To apply the filter to a specific action, add the filter as an attribute to the action:

public class ProductsController : ApiController
{
    [NotImplExceptionFilter]
    public Contact GetContact(int id)
    {
        throw new NotImplementedException("This method is not implemented");
    }
}

示例Example

要将筛选器应用到 controller 中的所有操作,请将筛选器作为特性添加到 controller 类:To apply the filter to all of the actions on a controller, add the filter as an attribute to the controller class:

[NotImplExceptionFilter]
public class ProductsController : ApiController
{
    // ...
}

示例Example

要将筛选器全局应用到所有 Web API 控制器,请将筛选器的实例添加到 GlobalConfiguration.Configuration.Filters 集合。To apply the filter globally to all Web API controllers, add an instance of the filter to the GlobalConfiguration.Configuration.Filters collection. 此集合中的异常筛选器将应用到任何 Web API 控制器操作。Exception filters in this collection apply to any Web API controller action.

GlobalConfiguration.Configuration.Filters.Add(
    new ProductStore.NotImplExceptionFilterAttribute());

示例Example

对于模型验证,可按如下所示,将模型状态传递给 CreateErrorResponse 方法:For model validation, the model state can be passed to CreateErrorResponse method as shown below:

public HttpResponseMessage PostProduct(Product item)
{
    if (!ModelState.IsValid)
    {
        return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ModelState);
    }
    // Implementation not shown...
}

请查看参考部分中的链接,了解有关 ASP.NET Web API 中异常处理和模型验证的更多详细信息Check the links in the references section for additional details about exceptional handling and model validation in ASP.NET Web API

不要在错误消息中公开安全详细信息Do not expose security details in error messages

标题Title 详细信息Details
组件Component Web 应用程序Web Application
SDL 阶段SDL Phase 构建Build
适用的技术Applicable Technologies 泛型Generic
属性Attributes 空值N/A
参考References 空值N/A
步骤Steps

常规错误消息将直接提供给用户,其中不包含敏感的应用程序数据。Generic error messages are provided directly to the user without including sensitive application data. 敏感数据的示例包括:Examples of sensitive data include:

  • 服务器名称Server names
  • 连接字符串Connection strings
  • 用户名Usernames
  • 密码Passwords
  • SQL 过程SQL procedures
  • 动态 SQL 失败详细信息Details of dynamic SQL failures
  • 堆栈跟踪和代码行Stack trace and lines of code
  • 内存中存储的变量Variables stored in memory
  • 驱动器和文件夹位置Drive and folder locations
  • 应用程序安装点Application install points
  • 主机配置设置Host configuration settings
  • 其他内部应用程序详细信息Other internal application details

捕获应用程序中的所有错误、提供常规错误消息,以及在 IIS 中启用自定义错误,都有助于防止信息泄漏。Trapping all errors within an application and providing generic error messages, as well as enabling custom errors within IIS will help prevent information disclosure. SQL Server 数据库和 .NET 异常处理以及其他错误处理体系结构提供的信息特别详细,能够为恶意用户分析应用程序带来极大的帮助。SQL Server database and .NET Exception handling, among other error handling architectures, are especially verbose and extremely useful to a malicious user profiling your application. 不要直接显示派生自 .NET Exception 类的类的内容,确保采取适当的异常处理,避免无意中向用户直接引发意外的异常。Do not directly display the contents of a class derived from the .NET Exception class, and ensure that you have proper exception handling so that an unexpected exception isn't inadvertently raised directly to the user.

  • 直接向用户提供常规错误消息时,应该去掉异常/错误消息中直接显示的具体详细信息。Provide generic error messages directly to the user that abstract away specific details found directly in the exception/error message
  • 不要直接向用户显示 .NET exception 类的内容Do not display the contents of a .NET exception class directly to the user
  • 捕获所有错误消息,在适当的情况下,通过发送到应用程序客户端的常规错误消息来通知用户Trap all error messages and if appropriate inform the user via a generic error message sent to the application client
  • 不要直接向用户公开 Exception 类的内容,尤其是 .ToString() 的返回值,或者 Message 或 StackTrace 属性的值。Do not expose the contents of the Exception class directly to the user, especially the return value from .ToString(), or the values of the Message or StackTrace properties. 安全记录此信息,向用户显示一般性的消息Securely log this information and display a more innocuous message to the user

实现默认错误处理页Implement Default error handling page

标题Title 详细信息Details
组件Component Web 应用程序Web Application
SDL 阶段SDL Phase 构建Build
适用的技术Applicable Technologies 泛型Generic
属性Attributes 空值N/A
参考References 编辑 ASP.NET 错误页设置对话框Edit ASP.NET Error Pages Settings Dialog Box
步骤Steps

如果 ASP.NET 应用程序失败并导致 HTTP/1.x 500 内部服务器错误,或者某项功能配置(例如请求筛选)阻止显示页面,会生成错误消息。When an ASP.NET application fails and causes an HTTP/1.x 500 Internal Server Error, or a feature configuration (such as Request Filtering) prevents a page from being displayed, an error message will be generated. 管理员可以选择是要让应用程序向客户端显示友好的消息、向客户端显示详细的错误消息,还是只向 localhost 显示详细的错误消息。Administrators can choose whether or not the application should display a friendly message to the client, detailed error message to the client, or detailed error message to localhost only. web.config 中的 <customErrors> 标记有三种模式:The <customErrors> tag in the web.config has three modes:

  • On: 指定启用自定义错误。On: Specifies that custom errors are enabled. 如果未指定 defaultRedirect 特性,用户将看到常规错误。If no defaultRedirect attribute is specified, users see a generic error. 将向远程客户端和本地主机显示自定义错误The custom errors are shown to the remote clients and to the local host
  • Off: 指定禁用自定义错误。Off: Specifies that custom errors are disabled. 将向远程客户端和本地主机显示详细的 ASP.NET 错误The detailed ASP.NET errors are shown to the remote clients and to the local host
  • RemoteOnly: 指定只向远程客户端显示自定义错误,向本地主机显示 ASP.NET 错误。RemoteOnly: Specifies that custom errors are shown only to the remote clients, and that ASP.NET errors are shown to the local host. 此为默认值。This is the default value

打开应用程序/站点的 web.config文件,确保该标记中定义了 <customErrors mode="RemoteOnly" /><customErrors mode="On" />Open the web.config file for the application/site and ensure that the tag has either <customErrors mode="RemoteOnly" /> or <customErrors mode="On" /> defined.

在 IIS 中将部署方法设置为 RetailSet Deployment Method to Retail in IIS

标题Title 详细信息Details
组件Component Web 应用程序Web Application
SDL 阶段SDL Phase 部署Deployment
适用的技术Applicable Technologies 泛型Generic
属性Attributes 空值N/A
参考References 部署元素(ASP.NET 设置架构)deployment Element (ASP.NET Settings Schema)
步骤Steps

<deployment retail> 开关旨在由 IIS 生产服务器使用。The <deployment retail> switch is intended for use by production IIS servers. 使用此开关可以帮助应用程序尽量以最佳的性能运行,同时,可通过禁用应用程序在页面中生成跟踪输出的功能、禁用向最终用户显示详细错误消息的功能,以及禁用调试开关,来最大程度地减少泄露安全信息的可能性。This switch is used to help applications run with the best possible performance and least possible security information leakages by disabling the application's ability to generate trace output on a page, disabling the ability to display detailed error messages to end users, and disabling the debug switch.

通常,主要面向开发人员的开关和选项(例如,对失败的请求进行跟踪和调试)是在现行开发的过程中启用的。Often times, switches and options that are developer-focused, such as failed request tracing and debugging, are enabled during active development. 建议将任何生产服务器上的部署方法设置为 retail。It is recommended that the deployment method on any production server be set to retail. 打开 machine.config 文件,确保 <deployment retail="true" /> 保持设置为 true。open the machine.config file and ensure that <deployment retail="true" /> remains set to true.

异常应安全失败Exceptions should fail safely

标题Title 详细信息Details
组件Component Web 应用程序Web Application
SDL 阶段SDL Phase 构建Build
适用的技术Applicable Technologies 泛型Generic
属性Attributes 空值N/A
参考References 安全失败Fail securely
步骤Steps 应用程序应安全失败。Application should fail safely. 对于根据所做的特定决策返回布尔值的任何方法,应该谨慎创建异常块。Any method that returns a Boolean value, based on which certain decision is made, should have exception block carefully created. 许多逻辑错误就是因为在编写异常块时漫不经心,使安全问题日积月累造成的。There are lot of logical errors due to which security issues creep in, when the exception block is written carelessly.

示例Example

        public static bool ValidateDomain(string pathToValidate, Uri currentUrl)
        {
            try
            {
                if (!string.IsNullOrWhiteSpace(pathToValidate))
                {
                    var domain = RetrieveDomain(currentUrl);
                    var replyPath = new Uri(pathToValidate);
                    var replyDomain = RetrieveDomain(replyPath);

                    if (string.Compare(domain, replyDomain, StringComparison.OrdinalIgnoreCase) != 0)
                    {
                        //// Adding additional check to enable CMS urls if they are not hosted on same domain.
                        if (!string.IsNullOrWhiteSpace(Utilities.CmsBase))
                        {
                            var cmsDomain = RetrieveDomain(new Uri(Utilities.Base.Trim()));
                            if (string.Compare(cmDomain, replyDomain, StringComparison.OrdinalIgnoreCase) != 0)
                            {
                                return false;
                            }
                            else
                            {
                                return true;
                            }
                        }

                        return false;
                    }
                }

                return true;
            }
            catch (UriFormatException ex)
            {
                LogHelper.LogException("Utilities:ValidateDomain", ex);
                return true;
            }
        }

如果发生某种异常,上述方法始终返回 True。The above method will always return True, if some exception happens. 如果最终用户提供了浏览器支持的、但 Uri() 构造函数不支持的错误格式 URL,则会引发异常,受害者会转到有效但格式不当的 URL。If the end user provides a malformed URL, that the browser respects, but the Uri() constructor doesn't, this will throw an exception, and the victim will be taken to the valid but malformed URL.