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)