DEV Community

Cover image for Interceptando mensagens de erro com o nginx
Renan de Andrade
Renan de Andrade

Posted on

Interceptando mensagens de erro com o nginx

Como Desenvolvedores de Software, o cuidado com o quesito segurança de nossas apps é constante e nunca é demais. Sempre que uma de nossas aplicações se tornam públicas, temos o máximo de cuidado para não expor informações privadas e que possam comprometer o funcionamento da mesma, ou até mesmo do negócio como um todo.

Um ponto crucial de atenção que todos devemos ter ao expor uma aplicação para o público é como ela se comporta quando ocorre algum erro e o que é reportado para o usuário nesses cenários. Resposta de erros mal tratadas podem não ser nada demais para a maioria dos usuários, mas tenha certeza que para alguém, vai ser um prato cheio.

Com o intuito de ajudar a mitigar esses problemas, este artigo aborda o uso da feature proxy_intercept_errors do nginx.

O que é o nginx

O nginx é um web server largamente utilizado que pode ter muitas utilidades, as mais comuns são Load Balancer e Reverse Proxy. Nesses casos, podemos fazer diversas tratativas nos detalhes das requisições antes e depois do processamento por parte de nossas apps.

A imagem abaixo é uma representação básica de como o nginx pode ser utilizado:

Representação básica da arquitetura de uma app usando nginx

Substituindo mensagens de erro

Para vermos a interceptação de erros em ação, vamos criar um projetinho simples usando Go, nginx e docker-compose.

Você pode encontrar o repositório de exemplo aqui.

Aplicação

Nosso exemplo é uma aplicação simples em Go contendo um http server simples com as seguintes rotas e seus respectivos códigos de retorno:

  • /success, 200;
  • /bad_request, 400;
  • /not_found, 404;
  • /internal_server_error, 500.
package main import ( "net/http" "github.com/gofiber/fiber/v2" ) func main() { app := fiber.New() app.Get("/success", func(c *fiber.Ctx) error { c.Set("Content-Type", "application/json") return c.SendString(`{"message": "success"}`) }) app.Get("/bad_request", func(c *fiber.Ctx) error { c.Set("Content-Type", "application/json") return c.Status(http.StatusBadRequest).SendString(`{"error": "The new password cannot be the same as the previous one"}`) }) app.Get("/not_found", func(c *fiber.Ctx) error { c.Set("Content-Type", "application/json") return c.Status(http.StatusNotFound).SendString(`{"error":"This resource is not found at database 'vuln'"`) }) app.Get("/internal_server_error", func(c *fiber.Ctx) error { c.Set("Content-Type", "application/json") return c.Status(http.StatusInternalServerError).SendString(`{"error":"Error log with private information"`) }) app.Listen(":8000") } 
Enter fullscreen mode Exit fullscreen mode

A ideia é justamente podermos simular vários códigos de erro para testar nossa feat de interceptação de erros. Imagine que em um cenário real qualquer um desses retornos possa carregar uma informação que o cliente não possa ter acesso.

Configuração do nginx

De maneira bem simples, nosso arquivo de configuração do nginx ficou mais ou menos assim:

events{} http { server { listen 80; error_page 404 500 /custom_err.html; error_page 400 @error400; location / { # proxy_intercept_errors on; proxy_pass http://app:8000; } location = /custom_err.html { root /usr/share/nginx/html; internal; } location @error400 { default_type application/json; internal; return 400 '{"error": {"status_code": 400,"status": "Bad Request"}}'; } } } 
Enter fullscreen mode Exit fullscreen mode

Sim, a linha comentada é intencional.

Testando a aplicação

Antes de entendermos melhor essa como tudo funciona, vamos ver nosso exemplo em ação. Execute o seguinte comando para iniciar nossa aplicação:

$ make run 
Enter fullscreen mode Exit fullscreen mode

Assim que estiver tudo rodando, vamos testar um dos endpoints de erro:

$ curl -i localhost/not_found 
Enter fullscreen mode Exit fullscreen mode

O resultado deve ser esse:

HTTP/1.1 404 Not Found Server: nginx/1.25.3 Date: Sun, 12 Nov 2023 02:42:58 GMT Content-Type: application/json Content-Length: 56 Connection: keep-alive {"error":"This resource is not found at database 'vuln'" 
Enter fullscreen mode Exit fullscreen mode

E voila! Tivemos uma resposta com informações comprometedoras.

Interceptando erros

Removendo o comentário, nosso location /fica assim:

 #... location / { proxy_intercept_errors on; proxy_pass http://app:8000; } #... 
Enter fullscreen mode Exit fullscreen mode

Chamando o mesmo endpoint que antes, temos esse novo retorno:

HTTP/1.1 404 Not Found Server: nginx/1.25.3 Date: Sun, 12 Nov 2023 02:43:50 GMT Content-Type: text/html Content-Length: 250 Connection: keep-alive ETag: "654cd606-fa" <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Error page</title> </head> <body> <h1>Ops... This is a custom error page</h1> </body> </html> 
Enter fullscreen mode Exit fullscreen mode

Isso significa que o Nginx interceptou nosso retorno de erro e substituiu pelo retorno de nossa página de erro customizada. E segundo a documentação, esse recurso é desabilitado por padrão.

É possível também retornar alguns outros formatos de resposta, como quando executamos o comando:

$ curl -i localhost/bad_request 
Enter fullscreen mode Exit fullscreen mode

Que retorna a seguinte resposta:

HTTP/1.1 400 Bad Request Server: nginx/1.25.3 Date: Sun, 12 Nov 2023 02:57:55 GMT Content-Type: application/json Content-Length: 55 Connection: close {"error": {"status_code": 400,"status": "Bad Request"}} 
Enter fullscreen mode Exit fullscreen mode

Como já deu para perceber, a resposta a ser retornada é definida pela config error_page.

Conclusão

Por mais que seja uma configuração simples, a interceptação de erros por parte do nginx pode ajudar a mitigar vários problemas. Esse post demonstrou uma das muitas maneiras de utilizar esse recurso.

Não esqueça de testar você mesmo o nosso exemplo aqui apresentado e explorar as mais diversas formas de usar essa e outras features do nginx.

And keep learning!

Créditos de imagem:

Cover image: Photo by David Pupăză on Unsplash

Top comments (0)