Skip to main content

Prevenção de scripting entre sites (XSS) em aplicativos Java com Snyk Code

Escrito por:
wordpress-sync/Blog-Design_Snyk-Code-

25 de abril de 2023

0 minutos de leitura

O Java é uma linguagem de programação avançada para back-end que também pode ser usada para escrever páginas HTML de aplicativos web. No entanto, quando criam essas páginas, os desenvolvedores precisam conhecer os riscos de segurança associados a ataques de scripting entre sites (XSS). Com o surgimento das estruturas modernas de modelos, ficou mais fácil prevenir ataques de segurança usando técnicas adequadas de validação e codificação de entradas. Entretanto, quando os desenvolvedores optam por criar suas próprias páginas HTML sem usar uma estrutura de modelos, o risco de introdução de vulnerabilidades aumenta. 

Por exemplo, usar o objeto HttpServletResponse em um aplicativo Spring MVC para escrever conteúdo diretamente na resposta pode criar oportunidades para usuários mal-intencionados injetarem código na página, levando a possíveis ataques de XSS. Portanto, é essencial que os desenvolvedores tomem medidas para garantir que a segurança dos aplicativos web Java permaneça alta, implementando medidas apropriadas para prevenir vulnerabilidades de XSS durante a criação de páginas HTML.

Uma solução como a mostrada abaixo é uma maneira fácil de implementar uma página renderizada no servidor sem qualquer estrutura sofisticada que normalmente é acompanhada por instruções específicas. No entanto, há algumas desvantagens óbvias nesse método.

Escrever saídas HTML no Spring MVC sem uma estrutura de modelos

Vamos supor que você tenha um aplicativo web que recebe o nome de um produto e o exibe em uma página web usando o objeto HttpServletResponse. Este é um exemplo de como você pode implementar esse recurso em um controlador Spring MVC:

1@GetMapping("/direct")
2public void directLink (@RequestParam String param, HttpServletResponse response) throws IOException {
3   Product prod = productService.getProductByName(param);
4
5   response.setContentType("text/html");
6   var writer = response.getWriter();
7   writer.write(head);
8   writer.write("<div class=\"panel-heading\"><h1>"+ param + "</h1></div>");
9
10   String output = "<div class=\"panel-body\">" +
11           "<ul>" +
12           "<li>%s</li>" +
13           "<li>%s</li>" +
14           "<li>%s</li>" +
15           "</ul>" +
16           "</div>";
17
18   writer.write(String.format(output, prod.getDescription(), prod.getProductType(), prod.getPrice()));
19   writer.write(foot);
20
21   response.getWriter().flush();
22}

Você consegue identificar que tipo de vulnerabilidades de segurança podem ser introduzidas com o código Java acima?

Identificação de XSS com Snyk Code

Olhando atentamente a função acima, você pode já reconhecer pelo menos uma vulnerabilidade XSS e talvez até duas. Quando verificamos o aplicativo com o Snyk Code, recebemos notificações de dois problemas de XSS diferentes nesse método.

Há várias maneiras de utilizar o Snyk Code. Vamos dar uma olhada em três exemplos diferentes. Para um desenvolvedor, a forma mais direta de obter respostas do Snyk Code é instalando um plugin no IDE. Temos plugins disponíveis para vários IDEs diferentes. No exemplo a seguir, mostro como o plugin do IntelliJ me ajuda a encontrar problemas de SS durante o desenvolvimento.

Saída do plugin IntelliJ:

blog-preventing-xss-product-controller

Outra opção é executar o Snyk Code usando o Snyk CLI. A execução do comando snyk code test no terminal gera uma saída como a mostrada abaixo. Esse método é útil em máquinas locais ou como parte da compilação automática em um pipeline de CI/CD.

Saída da CLI:

blog-preventing-xss-high-vulns

A terceira opção que quero mostrar é a interface de usuário web. Para obter essa saída, usei a integração do Git com a Snyk e conectei meu repositório do GitHub à interface web da Snyk usando o painel em https://app.snyk.io. Essa solução verifica o código confirmado no meu repositório em busca de vulnerabilidades de segurança. 

Saída da interface de usuário web:

blog-preventing-xss-snyk-code-report

Todas as três opções de verificação diferentes me mostram que há dois problemas diferentes de segurança contra XSS que preciso resolver. E o Snyk Code aponta a localização exata delas no meu código. Vamos analisá-las e ver como podemos mitigá-las.

XSS reflexivo 

O XSS reflexivo é um tipo de ataque de XSS que ocorre quando um usuário injeta código malicioso em um aplicativo web. Esse código é refletido de volta para o usuário como parte de uma resposta. No exemplo que mostrei, se a entrada do usuário não foi devidamente validada ou sanitizada antes de ser escrita na resposta, um usuário mal-intencionado poderia injetar um script que seria executado por outros usuários que visualizam a página web. Frequentemente, esse tipo de ataque de XSS é usado para roubar dados do usuário, modificar o conteúdo do site ou realizar outras ações mal-intencionadas.

O código acima recupera o nome do usuário do parâmetro da solicitação HTTP e o escreve diretamente no objeto HttpServletResponse usando:

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

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

Por exemplo, .../direct?param=<script>alert(document.cookie);</script> poderia revelar informações pessoais de um cookie. Isso significa que essas informações também podem ser enviadas para outro servidor sem que você saiba.

O Snyk Code detectou esse erro para mim, indicando o XSS na linha 93.

XSS armazenado

Por outro lado, o XSS armazenado é um tipo de ataque de XSS onde o código malicioso é 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 foi devidamente validada ou sanitizada e foi armazenada em um banco de dados, um usuário mal-intencionado poderia injetar um script que seria enviado a todos os usuários que visualizam a página web. Esse tipo de ataque de XSS pode ser particularmente perigoso porque tem potencial para afetar muitos usuários e persistir mesmo depois da correção da injeção inicial.

O código acima recupera um produto de ProductService e o exibe nos campos como parte da string de saída. No entanto, esse código é vulnerável a ataques de XSS armazenado porque não valida ou sanitiza corretamente a entrada recebida do banco de dados. A sanitização é particularmente importante se você não sabe ao certo quem tem permissão de gravação no banco de dados. Por exemplo, um usuário mal-intencionado poderia enviar uma descrição de produto que inclui código HTML ou JavaScript. Essa descrição seria armazenada no banco de dados e enviada a todos os usuários que acessam a visualização do produto. 

O Snyk Code apontou esse possível problema de XSS na linha 103, onde inserimos o product.description na string de saída sem qualquer validação ou sanitização.

Como mitigar vulnerabilidades de XSS com Snyk Code

Para prevenir vulnerabilidades de XSS, é importante validar e sanitizar adequadamente a entrada do usuário antes de escrevê-la na resposta. O Snyk Code já nos ajuda apontando possíveis soluções.  Uma forma de fazer isso é usando uma biblioteca como a Apache Commons Text para codificar a entrada e prevenir a execução de código malicioso.

blog-preventing-xss-string-path

Usando a função escapeHtml4(), podemos garantir que o código de XSS reflexivo e armazenado seja ignorado, evitando a execução na carga da página.

Obviamente, outras bibliotecas podem tomar medidas similares. Além da Apache Commons Text, você pode dar uma olhada na OWASP Encoder. Se você trabalha com Spring, também pode usar a HtmlUtils.htmlEscape da Spring.

Usando a Apache Commons Text, o código que ignora corretamente o XSS pode ficar assim:

1@GetMapping("/direct")
2public void directLink (@RequestParam String param, HttpServletResponse response) throws IOException {
3   Product prod = productService.getProductByName(param);
4
5   response.setContentType("text/html");
6   var writer = response.getWriter();
7   writer.write(head);
8   writer.write("<div class=\"panel-heading\"><h1>"+ StringEscapeUtils.escapeHtml4(param) + "</h1></div>");
9
10   String output = "<div class=\"panel-body\">" +
11           "<ul>" +
12           "<li>%s</li>" +
13           "<li>%s</li>" +
14           "<li>%s</li>" +
15           "</ul>" +
16           "</div>";
17
18   writer.write(String.format(output,
19             StringEscapeUtils.escapeHtml4(prod.getDescription()),
20             StringEscapeUtils.escapeHtml4(prod.getProductType()),
21             StringEscapeUtils.escapeHtml4(prod.getPrice())));
22
23   writer.write(foot);

Cuidado com estruturas de geração de modelos

Estruturas de modelos como o Thymeleaf podem ajudar a proteger contra vulnerabilidades de XSS. O Thymeleaf é um mecanismo de geração de modelos popular para Java com suporte incorporado para ignorar HTML, o que é útil para prevenir ataques de XSS com a codificação de todas as entradas do usuário incluídas no HTML renderizado.

No entanto, sua eficácia depende muito da forma como você cria o modelo. Por exemplo, esta é uma maneira de usar o Thymeleaf para renderizar um produto semelhante ao exemplo anterior:

1<div class="product" th:each="product : ${products}">
2  <p th:text="${product.name}"></p>
3  <p th:text="${product.type}"></p>
4  <p th:text="${product.pric}"></p>
5  <p th:utext="${product.descriptiont}"></p>
6</div>

Nesse exemplo, os atributos th:text serão ignorados, mas o atributo th:utext não. Esse atributo th:utext renderiza o texto do comentário sem ignorar qualquer tag HTML ou caractere especial, o que representa um risco de vulnerabilidade ao XSS. Para usar uma estrutura específica, é essencial conhecer o comportamento de alguns elementos.

Detecção de XSS antes da implantação em produção

A prevenção de ataques de XSS é uma preocupação essencial para desenvolvedores que trabalham em aplicativos web Java. É crucial identificar e resolver vulnerabilidades de XSS o mais cedo possível no processo de desenvolvimento. Embora a sanitização da entrada do usuário possa mitigar eficazmente os ataques de XSS, nem sempre isso será suficiente.

Consulte recursos como a OWASP XSS Cheat Sheet e a aula do Snyk Learn sobre XSS para acompanhar as mais recentes ameaças e práticas recomendadas de prevenção de XSS.

Além disso, é importante utilizar as ferramentas certas para identificar erros 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 no início do ciclo de desenvolvimento. Adotando uma abordagem proativa para a prevenção de XSS e usando os recursos e ferramentas corretos, os desenvolvedores podem ajudar a garantir a segurança e integridade dos aplicativos web Java.