Como evitar vulnerabilidades na análise de YAML com snakeyaml em Java
30 de março de 2021
0 minutos de leituraO que é YAML?
YAML é uma linguagem legível por pessoas para serializar dados, comumente usada em arquivos de configuração. A palavra YAML é um acrônimo de “YAML ain’t a markup language” (YAML não é uma linguagem de marcação) e foi lançada pela primeira vez em 2001. Você pode comparar YAML com JSON ou XML, pois todos são formatos estruturados baseados em texto.
Quais são as diferenças entre YAML, JSON e XML?
Embora seja semelhante a essas linguagens, YAML foi projetado para ser mais legível que JSON e menos verboso que XML. Por exemplo, as três linguagens têm sintaxes diferentes para lidar com estrutura e aninhamento, mas YAML usa espaços em branco na indentação para isso.
Arquivos YAML são frequentemente usados para configurar aplicações, servidores de aplicação ou clusters. É um formato muito comum em aplicações Spring Boot e, claro, na configuração do Kubernetes. No entanto, assim como JSON e XML, você também pode usar YAML para serializar e desserializar dados.
Embora YAML pareça uma excelente alternativa ao XML e ao JSON, muitas pessoas não gostam muito de sua estrutura. Como a linguagem é baseada em linhas e usa indentação para representar estrutura e aninhamento, a indentação costuma causar problemas ao analisar estruturas de dados complexas. Um único espaço em branco ausente (ou a mais) em uma estrutura complexa e com muitos dados causa falhas na análise de YAML. Isso gera problemas inesperados, e é difícil encontrar o erro em um arquivo YAML.
O mais importante é observar que importar manualmente YAML para sua aplicação Java usando uma versão desatualizada do snakeyaml pode causar problemas.
Resumo
A versão desatualizada do snakeyaml contém uma vulnerabilidade de negação de serviço. Recomendamos fortemente atualizar o snakeyaml para a versão 1.26 ou superior para evitar esse problema.
Como analisar arquivos YAML em Java com snakeyaml.
Para analisar arquivos YAML na sua aplicação Java, você pode usar a conhecida biblioteca snakeyaml. Essa biblioteca é simples e leve, e permite converter YAML em objetos e vice-versa.
Vamos nos concentrar em ler YAML no nosso programa Java. Basicamente, há duas maneiras de fazer isso. A primeira é a forma genérica de ler uma entrada YAML com snakeyaml. No trecho abaixo, vou ler um arquivo YAML da pasta de recursos, que está no meu classpath.
Ao carregar o YAML dessa forma, o resultado será um LinkedHashMap que representa o arquivo YAML de maneira estruturada. Isso significa que ele pode conter qualquer coisa, de qualquer tipo, pois é uma estrutura genérica não vinculada a um tipo específico.
A segunda maneira de ler YAML é mais específica. Você pode analisar sua entrada YAML e convertê-la em um objeto determinado. O Snakeyaml tentará associar as variáveis do YAML aos campos do objeto com base nas convenções de nomenclatura. Isso causará uma exceção se o arquivo YAML não corresponder à estrutura do objeto ou ao objeto de destino da desserialização. No trecho abaixo, vou analisar minha entrada YAML e convertê-la para o tipo Person.
Person.java
Person.yaml
As duas formas de analisar YAML e convertê-lo em um objeto funcionam perfeitamente. Se você tiver absoluta certeza sobre o formato esperado da entrada, poderá convertê-la em um objeto específico. Caso contrário, talvez seja melhor usar a forma genérica e pesquisar a lista manualmente.
Ataque Billion Laughs em YAML
Um dos recursos do YAML é permitir a criação de âncoras. Você pode reutilizar essas âncoras em diferentes partes do arquivo, sem precisar repetir informações. No exemplo simplificado abaixo, crio duas variáveis: var1 e var2. Usando âncoras, var2 tem o mesmo valor que var1.
Vamos levar isso ao extremo e criar o famoso ataque Billion Laughs em YAML. Aplicando esse conceito de forma aninhada, posso de fato criar um bilhão de risadas.
Como você pode ver, lol1 é uma lista de 10 strings "lol". A variável lol2 é uma lista com 10 ocorrências de lol1. Ao repetir esse princípio várias vezes, chegamos a lolz = 10^9 ocorrências de "lol". Ou seja, um bilhão de risadas.
Com âncoras, você pode criar uma bomba YAML! A enorme quantidade de objetos (aninhados) criada por essa entrada YAML causará uma sobrecarga de memória.
No caso do snakeyaml, especialmente nas versões anteriores à 1.26, isso pode ser um problema. Se você analisar o YAML da forma genérica descrita no primeiro exemplo, essa bomba YAML resultará em um java.lang.OutOfMemoryError por falta de espaço no heap do Java. Normalmente, isso significa que sua aplicação trava e fica indisponível, causando uma negação de serviço.
No entanto, se você analisar o YAML e convertê-lo em um objeto específico, como no segundo exemplo, o problema pode parecer menos grave. A biblioteca snakeyaml tenta associar o nome da variável a um campo do seu objeto. Como isso é impossível, você receberá uma exceção de YAML. Embora possa parecer uma boa solução, ela não é infalível.
Digamos que temos um tipo person com um firstname e um lastname, como antes. Além disso, ele também pode conter children, representados por uma coleção do tipo person.
Agora tenho o mesmo problema do ataque Billion Laughs original. Posso criar um arquivo YAML semelhante, usando âncoras que passam por várias camadas. O arquivo YAML poderia ser assim. Observe que você não precisa preencher todos os campos de um tipo.
Criei vários filhos para o pai raiz, e os filhos de cada um apontam para uma âncora anterior, criando um efeito bola de neve. Portanto, independentemente de eu analisar o arquivo YAML de forma genérica ou convertê-lo especificamente para o objeto Person, acabarei sobrecarregando o heap.
Como corrigir ataques Billion Laughs em YAML no Java
A solução para esse problema é muito mais fácil do que você imagina e bem menos trabalhosa do que encontrar o espaço em branco que falta em um arquivo YAML. Basta atualizar sua versão do snakeyaml para 1.26 ou superior. A equipe do snakeyaml fez um ótimo trabalho ao corrigir esse problema, limitando a 50 o número de aliases para nós não escalares. Ao analisar uma bomba YAML como a descrita anteriormente com a versão mais recente do snakeyaml, você receberá apenas uma exceção com esta mensagem. Isso também significa que o heap não vai transbordar e sua aplicação continuará funcionando.
Isso mostra mais uma vez como é importante acompanhar as bibliotecas das quais você depende. Neste caso, atualizar para a versão mais recente resolve o problema. Como você deve saber, a Snyk pode ajudar: basta conectar seu repositório de código. Além disso, continue analisando as bibliotecas das quais você depende com Snyk Open Source para não ser pego de surpresa por vulnerabilidades como essa.
Comece a resolver desafios de capture the flag
Aprenda a resolver desafios de capture the flag assistindo sob demanda ao nosso workshop virtual introdutório.



