Zpracování chyb
- Zpracování chyb je součástí celkového zabezpečení aplikace
- Útok většinou začíná fází průzkumu, kdy se útočník snaží získat co nejvíce informací (verze, frameworky, knihovny)
- Neošetřené chyby mohou útočníkovi v této fázi výrazně pomoci
Kontext
- Probémy na úrovni zpracování chyb mohou odhalit mnoho informací o cíli
- Níže je uveden příklad technologického stacku (Struts2, Tomcat), prostřednictvím výjimky zobrazené uživateli
HTTP Status 500 - For input string: "null"
type Exception report
message For input string: "null"
description The server encountered an internal error that prevented it from fulfilling this request.
exception
java.lang.NumberFormatException: For input string: "null"
java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
java.lang.Integer.parseInt(Integer.java:492)
java.lang.Integer.parseInt(Integer.java:527)
sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
java.lang.reflect.Method.invoke(Method.java:606)
com.opensymphony.xwork2.DefaultActionInvocation.invokeAction(DefaultActionInvocation.java:450)
com.opensymphony.xwork2.DefaultActionInvocation.invokeActionOnly(DefaultActionInvocation.java:289)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:252)
org.apache.struts2.interceptor.debugging.DebuggingInterceptor.intercept(DebuggingInterceptor.java:256)
com.opensymphony.xwork2.DefaultActionInvocation.invoke(DefaultActionInvocation.java:246)
...
note: The full stack trace of the root cause is available in the Apache Tomcat/7.0.56 logs.
- Níže je uveden příklad odhalení chyby SQL dotazu spolu s cestou k instalaci webu, který lze použít k identifikaci místa injektáže
Warning: odbc_fetch_array() expects parameter /1 to be resource, boolean given
in D:\app\index_new.php on line 188
- V některých případech je efektivnější definovat error handler jako součást kódu
- Pokud dojde k neočekávané chybě, aplikace vždy vrátí obecnou odpověď, ale podrobnosti jsou zaznamenány pouze na straně serveru za účelem dalšího prošetření
- Aplikace by se měla snažit pokrýt všechny způsoby selhání a používat chyby 5xx pouze k označení odpovědí na požadavky, které nemůže splnit
- Aplikace by neměla poskytovat žádný obsah v odpovědi, který by odhalil podrobnosti o implementaci
- Aplikace by měla vhodně logovat a monitorovat
Návrh
Webová aplikace v Javě
- Nakonfigurujte globální error handler na úrovni deskriptoru nasazení
web.xml
- Následující konfiguraci je možné použít od specifikace Servlet 2.5 a vyšší
- Jakákoli neočekávaná chyba při této konfiguraci způsobí přesměrování na
error.jsp
, kde bude chyba zalogována a vrácena bude obecná odpověď web.xml
konfigurace
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" ns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
...
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/error.jsp</location>
</error-page>
...
</web-app>
- Obsah souboru
error.jsp
<%@ page language="java" isErrorPage="true" contentType="application/json; charset=UTF-8"
pageEncoding="UTF-8"%>
<%
String errorMessage = exception.getMessage();
// Log the exception via the content of the implicit variable named "exception"
// ...
// We build a generic response with a JSON format because we are in a REST API app context
// We also add an HTTP response header to indicate to the client app that the response is an error
response.setHeader("X-ERROR", "true");
// Note that we're using an internal server error response
// In some cases it may be prudent to return 4xx error codes, when we have misbehaving clients
response.setStatus(500);
%>
{"message":"An error occur, please retry"}
Java SpringMVC / SpringBoot aplikace
- Pomocí SpringMVC nebo SpringBoot je možné definovat globální error handler tak, že se do projektu implementuje následující třída
import net.minidev.json.JSONObject;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
/**
* Global error handler in charge of returning a generic response in case of unexpected error situation.
*/
@ControllerAdvice
public class RestResponseEntityExceptionHandler {
@ExceptionHandler(value = {Exception.class})
public ResponseEntity<Object> handleGlobalError(RuntimeException exception, WebRequest request) {
// Log the exception via the content of the parameter named "exception"
// ...
// We build a generic response with a JSON format because we are in a REST API app context
// We also add an HTTP response header to indicate to the client app that the response is an error
HttpHeaders responseHeaders = new HttpHeaders();
responseHeaders.setContentType(MediaType.APPLICATION_JSON);
responseHeaders.set("X-ERROR", "true");
JSONObject responseBody = new JSONObject();
responseBody.put("message", "An error occur, please retry");
// Note that we're using an internal server error response
// In some cases it may be prudent to return 4xx error codes, if we have misbehaving clients
ResponseEntity<JSONObject> response = new ResponseEntity<>(responseBody, responseHeaders,
HttpStatus.INTERNAL_SERVER_ERROR);
return (ResponseEntity) response;
}
}
Webová aplikace ASP NET Core
- Pro definování globálního error handleru zde existuje vyhrazený API controller
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Net;
namespace MyProject.Controllers
{
/// <summary>
/// API Controller used to intercept and handle all unexpected exception
/// </summary>
[Route("api/[controller]")]
[ApiController]
[AllowAnonymous]
public class ErrorController : ControllerBase
{
/// <summary>
/// Action that will be invoked for any call to this Controller in order to handle the current error
/// </summary>
/// <returns>A generic error formatted as JSON because we are in a REST API app context</returns>
[HttpGet]
[HttpPost]
[HttpHead]
[HttpDelete]
[HttpPut]
[HttpOptions]
[HttpPatch]
public JsonResult Handle()
{
// Get the exception that has implied the call to this controller
Exception exception = HttpContext.Features.Get<IExceptionHandlerFeature>()?.Error;
// Log the exception via the content of the variable named "exception" if it is not NULL
// ...
// We build a generic response with a JSON format because we are in a REST API app context
// We also add an HTTP response header to indicate to the client app that the response
// is an error
var responseBody = new Dictionary<String, String>{ {
"message", "An error occur, please retry"
} };
JsonResult response = new JsonResult(responseBody);
// Note that we're using an internal server error response
// In some cases it may be prudent to return 4xx error codes, if we have misbehaving clients
response.StatusCode = (int)HttpStatusCode.InternalServerError;
Request.HttpContext.Response.Headers.Remove("X-ERROR");
Request.HttpContext.Response.Headers.Add("X-ERROR", "true");
return response;
}
}
}
- Definice mapování chyb na API controller pro zpracování výjimek v souboru
Startup.cs
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace MyProject
{
public class Startup
{
...
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
// First we configure the error handler middleware!
// We enable the global error handler in others environments than DEV
// because debug page are useful during implementation
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// Our global handler is defined on "/api/error" URL so we indicate to the
// exception handler to call this API controller
// on any unexpected exception raised by the application
app.UseExceptionHandler("/api/error");
// To customize the response content type and text, use the overload of
// UseStatusCodePages that takes a content type and format string.
app.UseStatusCodePages("text/plain", "Status code page, status code: {0}");
}
// We configure others middlewares, remember that the declaration order is important...
app.UseMvc();
// ...
}
}
}
ASP NET Web API
- Pomocí tohoto rozhraní je možné definovat a registrovat obslužné programy
- Díky tomu je možné sledovat a zpracovávat všechny chyby, které se v aplikaci vyskytnou
- Definice obslužného programu pro sledování podrobností o chybě
using System;
using System.Web.Http.ExceptionHandling;
namespace MyProject.Security
{
/// <summary>
/// Global logger used to trace any error that occurs at application wide level
/// </summary>
public class GlobalErrorLogger : ExceptionLogger
{
/// <summary>
/// Method in charge of the management of the error from a tracing point of view
/// </summary>
/// <param name="context">Context containing the error details</param>
public override void Log(ExceptionLoggerContext context)
{
// Get the exception
Exception exception = context.Exception;
// Log the exception via the content of the variable named "exception" if it is not NULL
// ...
}
}
}
- Definice obsluhy pro správu chyby s cílem vrátit obecnou odpověď
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Web.Http.ExceptionHandling;
namespace MyProject.Security
{
/// <summary>
/// Global handler used to handle any error that occurs at application wide level
/// </summary>
public class GlobalErrorHandler : ExceptionHandler
{
/// <summary>
/// Method in charge of handle the generic response send in case of error
/// </summary>
/// <param name="context">Error context</param>
public override void Handle(ExceptionHandlerContext context)
{
context.Result = new GenericResult();
}
/// <summary>
/// Class used to represent the generic response send
/// </summary>
private class GenericResult : IHttpActionResult
{
/// <summary>
/// Method in charge of creating the generic response
/// </summary>
/// <param name="cancellationToken">Object to cancel the task</param>
/// <returns>A task in charge of sending the generic response</returns>
public Task<HttpResponseMessage> ExecuteAsync(CancellationToken cancellationToken)
{
// We build a generic response with a JSON format because we are in a REST API app context
// We also add an HTTP response header to indicate to the client app that the response
// is an error
var responseBody = new Dictionary<String, String>{ {
"message", "An error occur, please retry"
} };
// Note that we're using an internal server error response
// In some cases it may be prudent to return 4xx error codes, if we have misbehaving clients
HttpResponseMessage response = new HttpResponseMessage(HttpStatusCode.InternalServerError);
response.Headers.Add("X-ERROR", "true");
response.Content = new StringContent(JsonConvert.SerializeObject(responseBody),
Encoding.UTF8, "application/json");
return Task.FromResult(response);
}
}
}
}
- Registrace obou obslužných rutin v souboru
WebApiConfig.cs
using MyProject.Security;
using System.Web.Http;
using System.Web.Http.ExceptionHandling;
namespace MyProject
{
public static class WebApiConfig
{
public static void Register(HttpConfiguration config)
{
// Register global error logging and handling handlers in first
config.Services.Replace(typeof(IExceptionLogger), new GlobalErrorLogger());
config.Services.Replace(typeof(IExceptionHandler), new GlobalErrorHandler());
// Rest of the configuration
// ...
}
}
}
- Nastavení sekce
customErrors
do souboruWeb.config
:
<configuration>
...
<system.web>
<customErrors mode="RemoteOnly"
defaultRedirect="~/ErrorPages/Oops.aspx" />
...
</system.web>
</configuration>