通过


你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

安全帧:异常管理 |缓解措施

产品/服务 文章
WCF
Web API
  • 确保在 ASP.NET Web API
Web 应用程序

WCF - 不要在配置文件中包含 serviceDebug 节点

标题 详细信息
组件 WCF
SDL 阶段 构建
适用的技术 泛型、NET Framework 3
特性 不适用
参考 MSDN巩固王国
步骤 Windows通信框架(WCF)服务可配置为公开调试信息。 不应在生产环境中使用调试信息。 该 <serviceDebug> 标记定义是否为 WCF 服务启用调试信息功能。 如果属性 includeExceptionDetailInFaults 设置为 true,则应用程序的异常信息将返回到客户端。 攻击者可以利用他们从调试输出中获得的其他信息来装载针对应用程序使用的框架、数据库或其他资源的攻击。

示例

以下配置文件包括 <serviceDebug> 标记:

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

禁用服务中的调试信息。 这可以通过从应用程序的配置文件中删除 <serviceDebug> 标记来实现。

WCF - 不要在配置文件中包含 serviceMetadata 节点

标题 详细信息
组件 WCF
SDL 阶段 构建
适用的技术 常规
特性 泛型、NET Framework 3
参考 MSDN巩固王国
步骤 公开有关服务的信息可以向攻击者提供有关他们如何利用该服务的宝贵见解。 该 <serviceMetadata> 标记启用元数据发布功能。 服务元数据可能包含不应公开访问的敏感信息。 至少只允许受信任的用户访问元数据,并确保不公开不必要的信息。 更好的是,完全禁用发布元数据的功能。 安全 WCF 配置将不包含 <serviceMetadata> 标记。

确保在 ASP.NET Web API中执行正确的异常处理

标题 详细信息
组件 网络应用程序接口
SDL 阶段 构建
适用的技术 MVC 5、MVC 6
特性 不适用
参考 ASP.NET Web API 中的异常处理,ASP.NET Web API 中的模型验证
步骤 默认情况下,ASP.NET Web API中大多数未捕获的异常将转换为状态代码为 500, Internal Server Error 的 HTTP 响应

示例

若要控制 API 返回的状态代码, HttpResponseException 可以使用如下所示:

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

示例

若要进一步控制异常响应, HttpResponseMessage 可以使用类,如下所示:

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未经处理的异常,可以使用异常筛选器。 异常筛选器实现 System.Web.Http.Filters.IExceptionFilter 接口。 编写异常筛选器的最简单方法是从 System.Web.Http.Filters.ExceptionFilterAttribute 类派生并重写 OnException 方法。

示例

下面是将异常转换为 NotImplementedException HTTP 状态代码 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 异常筛选器:

  • 按操作
  • 通过控制器
  • 全球

示例

若要将筛选器应用于特定操作,请将筛选器添加为操作的属性:

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

示例

若要将筛选器应用于 controller 的所有操作,请将筛选器作为 controller 类的属性添加。

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

示例

若要将筛选器全局应用到所有 Web API 控制器,请将筛选器的实例添加到 GlobalConfiguration.Configuration.Filters 集合。 此集合中的异常筛选器适用于任何 Web API 控制器作。

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

示例

对于模型验证,可以将模型状态传递给 CreateErrorResponse 方法,如下所示:

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

请查看参考部分中的链接,了解有关 ASP.NET Web API中异常处理和模型验证的其他详细信息

不要在错误消息中公开安全详细信息

标题 详细信息
组件 Web 应用程序
SDL 阶段 构建
适用的技术 常规
特性 不适用
参考 不适用
步骤

将常规错误消息直接提供给用户,而无需包括敏感的应用程序数据。 敏感数据的示例包括:

  • 服务器名称
  • 连接字符串
  • 用户名
  • 密码
  • SQL 过程
  • 动态 SQL 失败的详细信息
  • 堆栈跟踪和代码行
  • 存储在内存中的变量
  • 驱动器和文件夹位置
  • 应用程序安装点
  • 主机配置设置
  • 其他内部应用程序详细信息

捕获应用程序中的所有错误并提供通用错误消息,以及在 IIS 中启用自定义错误将有助于防止信息泄露。 SQL Server数据库和.NET异常处理以及其他错误处理体系结构,对于恶意用户分析应用程序尤其详细和极其有用。 请勿直接显示从 .NET Exception 类派生的类的内容,并确保具备适当的异常处理机制,这样可以避免意外的异常直接显示给用户。

  • 向用户提供通用错误消息,隐藏异常/错误信息中的具体细节。
  • 不要直接向用户显示.NET异常类的内容
  • 捕获所有错误消息,并在适当情况下,通过向应用程序客户端发送通用错误消息的方式通知用户。
  • 不要直接向用户公开 Exception 类的内容,尤其是来自 .ToString()消息或 StackTrace 属性的返回值。 安全地记录此信息并向用户显示更无害的消息

实现默认错误处理页

标题 详细信息
组件 Web 应用程序
SDL 阶段 构建
适用的技术 常规
特性 不适用
参考 编辑 ASP.NET 错误页设置对话框
步骤

当 ASP.NET 应用程序失败并导致 HTTP/1.x 500 内部服务器错误或功能配置(如请求筛选)阻止显示页面时,将生成错误消息。 管理员可以选择应用程序是否应向客户端显示友好消息、向客户端显示详细的错误消息,还是仅向 localhost 显示详细的错误消息。 <customErrors>web.config 中的标记有三种模式:

  • 开启: 指定启用自定义错误。 如果未指定 defaultRedirect 属性,则用户会看到一般错误。 自定义错误会显示给远程客户端和本地主机
  • 关闭: 指定禁用自定义错误。 详细 ASP.NET 错误会显示给远程客户端和本地服务器
  • RemoteOnly:指定仅向远程客户端显示自定义错误,并将 ASP.NET 错误显示给本地主机。 这是默认值

打开web.config应用程序/站点的文件,并确保标记已定义<customErrors mode="RemoteOnly" /><customErrors mode="On" />

在 IIS 中将部署方法设置为 Retail

标题 详细信息
组件 Web 应用程序
SDL 阶段 部署
适用的技术 常规
特性 不适用
参考 deployment 元素(ASP.NET 配置架构)
步骤

<deployment retail> 交换机供生产 IIS 服务器使用。 此开关用于通过禁用应用程序在页面上生成跟踪输出、禁用向最终用户显示详细错误消息以及禁用调试开关,帮助应用程序以最佳性能和最少的安全信息泄漏运行。

通常,主要面向开发人员的开关和选项(例如,对失败的请求进行跟踪和调试)是在现行开发的过程中启用的。 建议将任何生产服务器上的部署方式设置为零售模式。 打开 machine.config 文件,确保 <deployment retail="true" /> 保持为 true。

异常应安全失败

标题 详细信息
组件 Web 应用程序
SDL 阶段 构建
适用的技术 常规
特性 不适用
参考 安全失效
步骤 应用程序应以安全的方式失败。 返回布尔值并用于做出某些决策的方法,应仔细设计异常处理块。 粗心编写异常块会导致许多逻辑错误,从而引发安全问题。

示例

        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。 如果最终用户提供格式不正确的 URL,浏览器会遵循该 URL,但 Uri() 构造函数不遵循,这将引发异常,受害者将转到有效但格式不正确的 URL。