XML e JSON são formatos de dados amplamente utilizados em APIs, configurações e troca de informações entre sistemas. Embora sejam simples e poderosos, o manuseio incorreto desses formatos pode introduzir graves vulnerabilidades de segurança, como injeção de entidades externas (XXE) e processamento de dados malformados. Esta aula explora os principais riscos e as melhores práticas para garantir a segurança ao lidar com XML e JSON.

Vamos abordar desde o parsing (análise) dos dados até a validação de esquemas, passando por ataques conhecidos e como se proteger. O foco é prático: você verá exemplos de código vulnerável e as correções necessárias para torná-lo seguro.

Parsing

Parsing (ou análise) é o processo de interpretar um documento XML ou JSON e convertê-lo em uma estrutura de dados utilizável pelo programa. Durante esse processo, o parser lê o conteúdo e o interpreta de acordo com as regras da linguagem. No entanto, parsers mal configurados podem ser explorados por atacantes para executar ações não intencionais, como acessar arquivos locais ou causar negação de serviço.

No contexto de XML, o padrão XML permite a definição de entidades personalizadas, que podem ser usadas para referenciar conteúdo externo. Se o parser não for configurado para desabilitar essas entidades, um atacante pode enviar um documento malicioso que faça o parser ler arquivos do servidor ou fazer requisições a serviços internos. Esse é o famoso ataque XXE (XML External Entity).

Para JSON, o parsing é geralmente mais seguro, pois o formato não possui um mecanismo de entidades externas. No entanto, parsers podem ter vulnerabilidades de implementação, como estouro de buffer ou consumo excessivo de memória, se não forem limitados em tamanho e profundidade. Portanto, é essencial usar bibliotecas de parsing atualizadas e configurá-las com limites adequados.

Exemplo de parsing seguro em Python com defusedxml para XML:

from defusedxml.ElementTree import parse

tree = parse('documento.xml')
root = tree.getroot()
print(root.tag)

Em contraste, o uso do parser padrão xml.etree.ElementTree é vulnerável a XXE. A biblioteca defusedxml desabilita entidades externas por padrão, protegendo o sistema.

Para JSON, em Python, o módulo json é seguro, mas é importante limitar o tamanho do payload e a profundidade do aninhamento para evitar ataques de DoS:

import json

dados = json.loads(texto)
# Considere usar limites de tamanho e profundidade

XXE

XXE (XML External Entity) é uma vulnerabilidade que ocorre quando um parser XML processa entidades externas definidas no documento. Essas entidades podem referenciar arquivos locais, URLs ou até mesmo executar comandos no servidor, dependendo da configuração. O ataque é possível quando o parser permite a resolução de entidades sem restrições.

Um exemplo clássico de documento XML malicioso que tenta ler o arquivo /etc/passwd:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE foo [
  <!ELEMENT foo ANY >
  <!ENTITY xxe SYSTEM "file:///etc/passwd" >
]>
<foo>&xxe;</foo>

Se o parser não estiver protegido, ele substituirá &xxe; pelo conteúdo do arquivo, expondo informações sensíveis.

Para mitigar o XXE, as seguintes medidas são essenciais:

  • Desabilitar entidades externas e DTDs no parser.
  • Usar bibliotecas que já são seguras por padrão, como defusedxml em Python ou libxml2 com opções seguras.
  • Validar e restringir o conteúdo do XML, rejeitando DOCTYPE e entidades personalizadas.

Exemplo de configuração segura em Java com o parser DocumentBuilderFactory:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);

Além disso, é importante validar o XML contra um esquema (XSD) para garantir que ele siga a estrutura esperada, mas isso não é suficiente para impedir XXE, pois a validação pode não rejeitar entidades.

JSON schema

JSON Schema é uma especificação que permite descrever a estrutura de dados JSON, definindo tipos, propriedades obrigatórias, intervalos de valores e outras restrições. Ele é análogo ao XSD para XML, mas adaptado ao formato JSON. O uso de JSON Schema é uma prática recomendada para validar a entrada de dados em APIs, garantindo que apenas dados bem formados e dentro das regras sejam aceitos.

Um exemplo simples de JSON Schema que valida um objeto com nome (string) e idade (inteiro):

{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "properties": {
    "nome": { "type": "string" },
    "idade": { "type": "integer", "minimum": 0 }
  },
  "required": ["nome", "idade"]
}

Com esse esquema, podemos validar se um JSON recebido está em conformidade. Por exemplo, o JSON {"nome": "Ana", "idade": 30} é válido, mas {"nome": "Ana"} não é, pois falta a idade.

A validação de JSON Schema não é uma medida de segurança por si só, mas ajuda a prevenir que dados inesperados ou malformados entrem no sistema, reduzindo a superfície de ataque. Por exemplo, se a aplicação espera um número e recebe uma string, isso pode causar erros não tratados ou até injeção de comandos se os dados forem usados em consultas.

Além disso, JSON Schema pode ser usado para limitar o tamanho de strings, números em faixas seguras e até mesmo padrões de expressões regulares, evitando valores extremos que possam causar problemas de desempenho.

Validation

Validação é o processo de verificar se os dados fornecidos estão de acordo com as regras esperadas antes de serem processados ou armazenados. No contexto de XML e JSON, a validação deve ser realizada em duas camadas: sintática e semântica. A validação sintática garante que o documento está bem formado e segue a estrutura definida (por exemplo, por um esquema). A validação semântica verifica se os valores fazem sentido no contexto da aplicação, como faixas de idade ou formatos de e-mail.

Para XML, a validação pode ser feita com XSD (XML Schema Definition) ou Relax NG. Um exemplo de XSD para um elemento pessoa:

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="pessoa">
    <xs:complexType>
      <xs:sequence>
        <xs:element name="nome" type="xs:string"/>
        <xs:element name="idade" type="xs:positiveInteger"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>
</xs:schema>

Para JSON, a validação com JSON Schema já foi apresentada. Além disso, é comum usar bibliotecas de validação específicas em cada linguagem, como jsonschema em Python ou Ajv em JavaScript.

É importante notar que a validação não deve ser a única camada de segurança. Ela deve ser combinada com outras práticas, como sanitização de entrada, uso de prepared statements para consultas SQL e controle de acesso. A validação ajuda a reduzir a chance de exploração de vulnerabilidades, mas não elimina todas.

Exemplo de validação de JSON Schema em Python:

import jsonschema
from jsonschema import validate

schema = {
    "type": "object",
    "properties": {
        "nome": {"type": "string"},
        "idade": {"type": "integer", "minimum": 0}
    },
    "required": ["nome", "idade"]
}

dados = {"nome": "João", "idade": 25}
validate(instance=dados, schema=schema)

Se os dados não forem válidos, uma exceção será lançada, e a aplicação pode tratá-la adequadamente.

Boas práticas e observações finais

Além das técnicas específicas para XML e JSON, algumas boas práticas gerais ajudam a melhorar a segurança:

  • Use bibliotecas de parsing confiáveis e mantenha-as atualizadas.
  • Configure os parsers com limites de tamanho e profundidade para evitar ataques de negação de serviço.
  • Sempre valide a entrada antes de processá-la, utilizando esquemas sempre que possível.
  • Prefira JSON em vez de XML quando possível, pois o JSON tem uma superfície de ataque menor.
  • Eduque sua equipe sobre os riscos de XXE e outras vulnerabilidades de parsing.

Lembre-se de que segurança é um processo contínuo. Testes de segurança, como fuzzing e análise estática, podem ajudar a identificar vulnerabilidades antes que sejam exploradas.

Referências

Exercícios

  1. Explique o que é o ataque XXE e cite duas medidas de mitigação.
  2. ✓ Resposta: O ataque XXE ocorre quando um parser XML processa entidades externas definidas no documento, permitindo que um atacante leia arquivos locais, faça requisições a serviços internos ou cause negação de serviço. Medidas de mitigação incluem desabilitar entidades externas e DTDs no parser, e usar bibliotecas seguras como defusedxml.
  3. Qual a diferença entre validação sintática e semântica? Dê um exemplo de cada para JSON.
  4. ✓ Resposta: Validação sintática verifica se o documento está bem formado e segue a estrutura definida (ex.: JSON Schema que exige que o campo "idade" seja um inteiro). Validação semântica verifica se os valores são significativos no contexto (ex.: idade não pode ser negativa). Exemplo: sintático – o campo "email" deve ser uma string; semântico – o email deve conter o caractere '@'.
  5. Escreva um JSON Schema que valide um objeto com campos "nome" (string), "idade" (inteiro entre 0 e 150) e "email" (string com formato de e-mail).
  6. ✓ Resposta:
    {
      "type": "object",
      "properties": {
        "nome": { "type": "string" },
        "idade": { "type": "integer", "minimum": 0, "maximum": 150 },
        "email": { "type": "string", "format": "email" }
      },
      "required": ["nome", "idade", "email"]
    }
  7. Em Python, usando defusedxml, como você faria para ler um XML com segurança? Escreva o código.
  8. ✓ Resposta:
    from defusedxml.ElementTree import parse
    
    tree = parse('arquivo.xml')
    root = tree.getroot()
    print(root.tag)
  9. Cite três boas práticas para evitar vulnerabilidades ao lidar com XML e JSON.
  10. ✓ Resposta: 1) Usar bibliotecas de parsing seguras e atualizadas. 2) Configurar limites de tamanho e profundidade para evitar DoS. 3) Validar a entrada com esquemas (XSD ou JSON Schema) e realizar validação semântica.