Skip to main content

Como prevenir Cross-Site Scripting (XSS) em aplicações Java com Snyk Code

Escrito por
Snyk Code - Static Application Security Testing re-imagined for the developer

25 de abril de 2023

0 minutos de leitura

Java é uma poderosa linguagem de programação de backend que também pode ser usada para criar páginas HTML para aplicações web. No entanto, ao criar essas páginas, os desenvolvedores precisam conhecer os possíveis riscos de segurança associados a ataques de Cross-Site Scripting (XSS). Com o avanço dos frameworks modernos de templates, ficou mais fácil prevenir ataques por meio de técnicas adequadas de validação e codificação de entradas. Porém, quando os desenvolvedores decidem criar suas próprias páginas HTML sem usar um framework de templates, o risco de introduzir vulnerabilidades aumenta.

Por exemplo, usar o objeto HttpServletResponse em uma aplicação Spring MVC para gravar conteúdo diretamente na resposta pode permitir que usuários mal-intencionados injetem código na página, levando a possíveis ataques de XSS. Portanto, é essencial que os desenvolvedores tomem medidas adequadas para manter a segurança das aplicações web Java e prevenir vulnerabilidades de XSS ao criar páginas HTML.

Uma solução como a mostrada abaixo é uma maneira simples de implementar uma página renderizada no servidor sem um framework sofisticado, que normalmente vem com instruções específicas. No entanto, é claro que esse método tem algumas desvantagens.

Como gerar saída HTML no Spring MVC sem um framework de templates

Imagine que você tenha uma aplicação web que recebe o nome de um produto e o exibe em uma página usando o objeto HttpServletResponse. Veja um exemplo de como implementar esse recurso em um controller do Spring MVC:

@GetMapping("/direct")
public void directLink (@RequestParam String param, HttpServletResponse response) throws IOException {
   Product prod = productService.getProductByName(param);

   response.setContentType("text/html");
   var writer = response.getWriter();
   writer.write(head);
   writer.write("<div class=\"panel-heading\"><h1>"+ param + "</h1></div>");

   String output = "<div class=\"panel-body\">" +
           "<ul>" +
           "<li>%s</li>" +
           "<li>%s</li>" +
           "<li>%s</li>" +
           "</ul>" +
           "</div>";

   writer.write(String.format(output, prod.getDescription(), prod.getProductType(), prod.getPrice()));
   writer.write(foot);

   response.getWriter().flush();
}

Você consegue identificar que tipos de vulnerabilidade de segurança podem ser introduzidos pelo código Java acima?

Como encontrar XSS com Snyk Code

Ao analisar atentamente a função acima, talvez você já reconheça pelo menos uma vulnerabilidade de XSS — e possivelmente até duas. Ao analisar minha aplicação com Snyk Code, recebemos alertas sobre dois problemas diferentes de XSS nesse método.

Há várias maneiras de aproveitar o Snyk Code. Vamos ver três exemplos diferentes. A forma mais direta de um desenvolvedor receber feedback do Snyk Code é instalar um plugin no IDE. Temos plugins disponíveis para vários IDEs. No exemplo a seguir, mostro como o plugin do IntelliJ me ajuda a encontrar problemas de XSS durante o desenvolvimento.

Resultado do plugin do IntelliJ:

Editor de código exibindo ProductController.java com duas vulnerabilidades de cross-site scripting sinalizadas nas linhas 93 e 103.

Outra opção é executar o Snyk Code pela Snyk CLI. Ao executar o comando snyk code test no terminal, você verá um resultado como o abaixo. Esse método é útil na sua máquina local ou como parte da compilação automatizada em um pipeline de CI/CD.

Resultado da CLI:

A saída do terminal lista duas vulnerabilidades de cross-site scripting (XSS) de alta gravidade em ProductController.java, nas linhas 93 e 103.

A terceira opção que quero mostrar é a interface web. Para obter esse resultado, usei a integração com Git e conectei meu repositório do GitHub à interface web do Snyk pelo dashboard em https://app.snyk.io. Essa solução analisa o código enviado ao meu repositório em busca de vulnerabilidades de segurança.

Resultado da interface web:

Relatório do Snyk Code mostrando duas vulnerabilidades de cross-site scripting (XSS), com código Java destacado e pontuação de 825.

As três opções de análise mostram que há dois problemas distintos de segurança relacionados a XSS que preciso corrigir — e o Snyk Code identifica exatamente onde eles estão no meu código. Vamos analisar cada um e ver como podemos mitigá-los.

XSS refletido

XSS refletido é um tipo de ataque de XSS que ocorre quando alguém injeta código malicioso em uma aplicação web, e esse código é devolvido ao usuário como parte da resposta. No exemplo que mostrei, se a entrada do usuário não fosse validada ou sanitizada corretamente antes de ser gravada na resposta, uma pessoa mal-intencionada poderia injetar um script que seria executado por outros usuários ao acessarem a página. Esse tipo de ataque de XSS costuma ser usado para roubar dados de usuários, modificar o conteúdo de sites ou realizar outras ações maliciosas.

O código acima recupera o nome do usuário a partir de um parâmetro da requisição HTTP e, em seguida, grava-o diretamente no objeto HttpServletResponse usando:

writer.write("<div class=\"panel-heading\"><h1>"+ param + "</h1></div>")

Esse código é vulnerável a ataques de XSS porque não valida nem sanitiza corretamente a entrada do usuário. Por exemplo, uma pessoa mal-intencionada poderia injetar código HTML ou JavaScript no parâmetro "name", que seria executado por outros usuários ao acessarem a página.

Por exemplo: .../direct?param=<script>alert(document.cookie);</script> pode revelar informações dos seus cookies pessoais. Isso significa que também podemos enviar essas informações para outro servidor sem que você saiba.

O Snyk Code detectou esse problema e apontou a vulnerabilidade de XSS na linha 93.

XSS armazenado

Já o XSS armazenado é um tipo de ataque em que o código malicioso fica armazenado no servidor e é enviado a todos os usuários que acessam a página afetada. No exemplo que mostrei, se a entrada do usuário não fosse validada ou sanitizada corretamente e fosse armazenada em um banco de dados, uma pessoa mal-intencionada poderia injetar um script que seria enviado a todos os usuários que acessassem a página afetada. Esse tipo de ataque pode ser especialmente perigoso, pois afeta muitos usuários e pode persistir mesmo depois que a injeção inicial é corrigida.

O código acima recupera um produto do ProductService e, em seguida, exibe seus campos como parte da string de saída. No entanto, esse código é vulnerável a ataques de XSS armazenado porque não valida nem sanitiza corretamente os dados recebidos do banco de dados. A sanitização é particularmente importante quando você não tem certeza de quem tem permissão para gravar no banco. Por exemplo, uma pessoa mal-intencionada poderia enviar uma descrição de produto contendo código HTML ou JavaScript, que seria armazenada no banco de dados e exibida a todos os usuários que visitassem a página do produto.

O Snyk Code apontou esse possível problema de XSS na linha 103, onde inserimos product.description na string de saída sem validá-lo nem sanitizá-lo.

Como mitigar vulnerabilidades de XSS com Snyk Code

Para prevenir vulnerabilidades de XSS, é importante validar e sanitizar corretamente as entradas do usuário antes de gravá-las na resposta. O Snyk Code já ajuda ao apontar possíveis soluções. Uma opção é usar uma biblioteca como Apache Commons Text para codificar a entrada e impedir a execução de código malicioso.

Diferença no código mostrando um caminho de solicitação escapado em HTML com StringEscapeUtils.escapeHtml4 para ajudar a prevenir ataques de cross-site scripting.

Com a função escapeHtml4(), podemos garantir que o código em casos de XSS refletido e armazenado seja escapado e não seja executado quando a página for carregada.

É claro que outras bibliotecas também podem fazer esse tipo de escape. Além de Apache Commons Text, você pode consultar o OWASP Encoder. Se você trabalha com Spring, também pode usar o HtmlUtils.htmlEscape do Spring.

Com o Apache Commons Text, o código devidamente escapado poderia ficar assim:

@GetMapping("/direct")
public void directLink (@RequestParam String param, HttpServletResponse response) throws IOException {
   Product prod = productService.getProductByName(param);

   response.setContentType("text/html");
   var writer = response.getWriter();
   writer.write(head);
   writer.write("<div class=\"panel-heading\"><h1>"+ StringEscapeUtils.escapeHtml4(param) + "</h1></div>");

   String output = "<div class=\"panel-body\">" +
           "<ul>" +
           "<li>%s</li>" +
           "<li>%s</li>" +
           "<li>%s</li>" +
           "</ul>" +
           "</div>";

   writer.write(String.format(output,
             StringEscapeUtils.escapeHtml4(prod.getDescription()),
             StringEscapeUtils.escapeHtml4(prod.getProductType()),
             StringEscapeUtils.escapeHtml4(prod.getPrice())));

   writer.write(foot);

Tenha cuidado com frameworks de templates

Frameworks de templates como o Thymeleaf podem ajudar a proteger contra vulnerabilidades de XSS. O Thymeleaf é um mecanismo de templates popular para Java, com suporte integrado para escape de HTML. Isso ajuda a prevenir ataques de XSS ao codificar todas as entradas do usuário incluídas no HTML renderizado.

No entanto, isso depende muito de como você cria o template. Por exemplo, veja como usar o Thymeleaf para renderizar um produto parecido com o do exemplo anterior:

<div class="product" th:each="product : ${products}">
  <p th:text="${product.name}"></p>
  <p th:text="${product.type}"></p>
  <p th:text="${product.pric}"></p>
  <p th:utext="${product.descriptiont}"></p>
</div>

Neste exemplo, os atributos th:text escapam o conteúdo, mas o atributo th:utext não. O atributo th:utext renderiza o texto do comentário sem escapar tags HTML nem caracteres especiais e, por isso, pode estar vulnerável a XSS. Ao usar um framework, é essencial saber como determinados elementos se comportam.

Detecte XSS antes de publicar em produção

Prevenir ataques de XSS é uma preocupação fundamental para quem desenvolve aplicações web Java. É essencial identificar e corrigir vulnerabilidades de XSS o quanto antes no processo de desenvolvimento. Embora a sanitização das entradas do usuário possa mitigar ataques de XSS de forma eficaz, nem sempre ela é suficiente.

Consulte recursos como o OWASP XSS Cheat Sheet e a lição do Snyk Learn sobre XSS para se manter atualizado sobre as ameaças mais recentes e as práticas recomendadas para prevenir XSS.

Além disso, é importante usar as ferramentas certas para detectar problemas de XSS e outras questões de segurança antes que cheguem à produção. O Snyk Code é uma ferramenta gratuita valiosa para identificar possíveis vulnerabilidades de segurança logo no início do ciclo de desenvolvimento. Com uma abordagem proativa para prevenir XSS e os recursos e as ferramentas adequados, os desenvolvedores podem ajudar a garantir a segurança e a integridade de suas aplicações web Java.

Comece a jogar Capture the Flag

Aprenda a resolver desafios de Capture the Flag assistindo à gravação sob demanda do nosso workshop virtual introdutório.