ASP.NET Core 6.0 ExceptionHandlerMiddleware:如何修复错误信息泄露问题?
ASP.NET Core 6.0 Web API 异常中间件安全问题处理
原异常处理中间件代码
public class ExceptionHandlerMiddleware { private readonly RequestDelegate next; public ExceptionHandlerMiddleware(RequestDelegate next) { this.next = next; } public async Task InvokeAsync(HttpContext context) { try { await this.next(context).ConfigureAwait(false); } catch (Exception ex) { await this.ConvertExceptionAsync(context, ex).ConfigureAwait(false); } } private Task ConvertExceptionAsync(HttpContext context, Exception exception) { HttpStatusCode httpStatusCode = HttpStatusCode.InternalServerError; context.Response.ContentType = "application/json"; var errorResponse = new ErrorResponse(); var result = string.Empty; switch (exception) { case ValidationException validationException: httpStatusCode = HttpStatusCode.BadRequest; errorResponse.Error.Errors = JsonSerializer.Serialize(validationException.ValidationErrors); break; case BadRequestException badRequestException: httpStatusCode = HttpStatusCode.BadRequest; errorResponse.Error.Message = badRequestException.Message; break; case NotFoundException notFoundException: httpStatusCode = HttpStatusCode.NotFound; break; case NotAcceptableException notAcceptableException: httpStatusCode = HttpStatusCode.NotAcceptable; errorResponse.Error.Message = notAcceptableException.Message; break; case Exception ex: httpStatusCode = HttpStatusCode.BadRequest; break; } errorResponse.ApiVersion = context.GetRequestedApiVersion()?.ToString(); errorResponse.Error.Code = (int)httpStatusCode; context.Response.StatusCode = (int)httpStatusCode; if (string.IsNullOrEmpty(result)) { errorResponse.Error.Message = exception.Message; } var serializeOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true, }; result = JsonSerializer.Serialize(errorResponse, serializeOptions); return context.Response.WriteAsync(result); } }
原错误响应格式
{ "error": { "code": 404, "message": "Education (a65f7f0c-2a29-4da0-bd4b-d737320730c6) is not found", "errors": "[]" }, "apiVersion": "1.0" }
安全扫描报告问题
该文件处理Exception或运行时错误ex。在异常处理代码中,应用在ConvertExceptionAsync方法中通过WriteAsync暴露了异常详情
避免通过错误信息泄露数据
- 不要向用户暴露异常输出数据
- 妥善处理各方法的异常
- 配置全局处理器以防止未处理错误
疑问
- 应该请求安全团队抑制该问题,还是可以在应用层面解决?
- 更新:将
context.Response.WriteAsync移至finally块(代码如下),是否能解决该安全问题?
修改后的代码
public async Task InvokeAsync(HttpContext context) { bool isExceptionReported = false; string result = string.Empty; try { await this.next(context).ConfigureAwait(false); } catch (Exception ex) { isExceptionReported = true; result = this.ConvertException(context, ex); } finally { if (isExceptionReported) { await context.Response.WriteAsync(result).ConfigureAwait(false); } } } private string ConvertException(HttpContext context, Exception exception) { HttpStatusCode httpStatusCode = HttpStatusCode.InternalServerError; context.Response.ContentType = "application/json"; var errorResponse = new ErrorResponse(); var result = string.Empty; switch (exception) { case ValidationException validationException: httpStatusCode = HttpStatusCode.BadRequest; errorResponse.Error.Errors = JsonSerializer.Serialize(validationException.ValidationErrors); break; case BadRequestException badRequestException: httpStatusCode = HttpStatusCode.BadRequest; errorResponse.Error.Message = badRequestException.Message; break; case NotFoundException notFoundException: httpStatusCode = HttpStatusCode.NotFound; break; case NotAcceptableException notAcceptableException: httpStatusCode = HttpStatusCode.NotAcceptable; errorResponse.Error.Message = notAcceptableException.Message; break; case Exception ex: httpStatusCode = HttpStatusCode.BadRequest; break; } errorResponse.ApiVersion = context.GetRequestedApiVersion()?.ToString(); errorResponse.Error.Code = (int)httpStatusCode; context.Response.StatusCode = (int)httpStatusCode; if (string.IsNullOrEmpty(result)) { errorResponse.Error.Message = exception.Message; } var serializeOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true, }; result = JsonSerializer.Serialize(errorResponse, serializeOptions); return result; }
解答
1. 不能请求安全团队抑制,必须在应用层面修复
安全扫描指出的是敏感信息泄露风险,属于可修复的代码缺陷,抑制问题会留下安全隐患,不符合安全规范。
2. 当前修改无法解决安全问题
把WriteAsync移到finally块只是改变了响应写入的时机,并没有改变异常信息直接返回给客户端的核心逻辑,通用Exception的Message依然可能包含内部敏感数据(如数据库错误、服务内部路径等),风险依然存在。
正确修复方案
(1)区分环境返回错误信息
注入IWebHostEnvironment,根据环境决定返回的错误内容:
- 开发环境:返回详细异常信息,方便调试
- 生产环境:只返回通用提示,不暴露任何内部细节
(2)完善异常分支处理
- 自定义异常(如
BadRequestException、NotFoundException):返回业务相关的友好提示,但避免泄露敏感ID或内部细节 - 通用
Exception:统一返回"服务器内部错误",同时将详细异常信息记录到日志(如Serilog、NLog),供排查问题使用
(3)优化错误响应内容
- 非必要情况下,移除
apiVersion这类可能暴露系统细节的字段 - 确保
Errors字段只返回客户端需要的验证信息,不包含内部错误堆栈
修复后的示例代码(核心部分)
// 新增构造注入IWebHostEnvironment和ILogger private readonly RequestDelegate next; private readonly IWebHostEnvironment _env; private readonly ILogger<ExceptionHandlerMiddleware> _logger; public ExceptionHandlerMiddleware(RequestDelegate next, IWebHostEnvironment env, ILogger<ExceptionHandlerMiddleware> logger) { this.next = next; _env = env; _logger = logger; } private string ConvertException(HttpContext context, Exception exception) { HttpStatusCode httpStatusCode = HttpStatusCode.InternalServerError; context.Response.ContentType = "application/json"; var errorResponse = new ErrorResponse(); switch (exception) { case ValidationException validationException: httpStatusCode = HttpStatusCode.BadRequest; errorResponse.Error.Errors = JsonSerializer.Serialize(validationException.ValidationErrors); errorResponse.Error.Message = "请求参数验证失败"; break; case BadRequestException badRequestException: httpStatusCode = HttpStatusCode.BadRequest; errorResponse.Error.Message = badRequestException.Message; break; case NotFoundException notFoundException: httpStatusCode = HttpStatusCode.NotFound; errorResponse.Error.Message = "请求资源不存在"; break; case NotAcceptableException notAcceptableException: httpStatusCode = HttpStatusCode.NotAcceptable; errorResponse.Error.Message = notAcceptableException.Message; break; case Exception ex: httpStatusCode = HttpStatusCode.InternalServerError; // 生产环境返回通用消息,开发环境返回原异常消息 errorResponse.Error.Message = _env.IsProduction() ? "服务器内部错误" : ex.Message; // 记录详细异常日志 _logger.LogError(ex, "未处理的异常"); break; } errorResponse.Error.Code = (int)httpStatusCode; context.Response.StatusCode = (int)httpStatusCode; var serializeOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, WriteIndented = true, }; return JsonSerializer.Serialize(errorResponse, serializeOptions); }
内容的提问来源于stack exchange,提问作者One Developer
相关产品推荐
相关产品推荐

