FlawOpen

flawopen.com/Entidades externas XML (XXE)/Segurança de parsers XML em Python

O xml.etree.ElementTree do Python é seguro contra XXE?

CWE-611: Referência a Entidade Externa XMLPágina de referência
Resposta curta

Para XXE clássico de arquivo e rede, os parsers integrados do Python baseados em Expat não acessam arquivos locais nem abrem conexões de rede por padrão. Isso não torna todo uso de XML seguro: versões do Expat anteriores à 2.7.2 podem sofrer DoS por expansão de entidades e tokens grandes. Os padrões do lxml também variam conforme a versão. Fixe e configure explicitamente os parsers ou use defusedxml para XML não confiável.

ARRISCADO — versão e opções do parser alteram a exposição

XXE (CWE-611):
O parser resolve uma entidade externa fornecida em XML controlado por atacante, podendo ler arquivos locais ou fazer requisições do servidor.
DoS por expansão de entidades:
Referências aninhadas ou repetidas consomem CPU ou memória em excesso durante a expansão.
Expat:
Parser C usado pelo XML integrado do Python; verifique a versão ativa com pyexpat.EXPAT_VERSION.
DTD:
Declarações de tipo de documento podem definir entidades e, conforme a configuração, solicitar recursos externos.
Etapa 1

XML não confiável chega ao parser

Um upload, SVG, mensagem SAML ou payload de integração cruza a fronteira de confiança e chega a uma API XML.

Etapa 2

Implementação e opções definem o comportamento

A biblioteca padrão usa Expat; lxml usa libxml2. Versão, expansão de entidades, acesso externo e flags determinam o que pode ocorrer.

Etapa 3

Resolução ou expansão produz efeito

Se a resolução externa estiver ativa, uma entidade pode buscar conteúdo local ou remoto. Expansão vulnerável ou tokens grandes podem consumir recursos.

Etapa 4

Permissões limitam o impacto

Permissões de arquivo e saída de rede limitam XXE; limites de entrada, parsers atualizados, flags seguras e isolamento reduzem a exposição.

A orientação atual do Python diz que Expat não acessa arquivos locais nem cria conexões de rede por padrão. Também alerta que versões anteriores à 2.7.2 podem sofrer ataques billion-laughs, quadratic-blowup, tokens grandes ou uso desproporcional de memória; confira pyexpat.EXPAT_VERSION, pois Python pode usar Expat incluído ou do sistema. Esse risco de exaustão depende da versão; não prova que ElementTree atual resolva entidades externas.

PROTEGIDO — rejeite DTDs e recursos externos
# Standard-library external access is off by default,
# but old Expat builds have resource-exhaustion risks.
import xml.etree.ElementTree as ET
from xml.parsers import expat

print(expat.EXPAT_VERSION)  # inspect the runtime parser
root = ET.fromstring(untrusted_xml)

# lxml before 5.0 defaulted resolve_entities=True.
# Explicit unsafe settings illustrate the risky boundary.
from lxml import etree
parser = etree.XMLParser(resolve_entities=True, load_dtd=True)
root = etree.fromstring(untrusted_xml, parser)
Três riscos distintos em parsers XML
from defusedxml.ElementTree import fromstring

# Reject DTD declarations when the format does not require them.
root = fromstring(untrusted_xml, forbid_dtd=True)

# If lxml is required, set its security options explicitly.
from lxml import etree
parser = etree.XMLParser(
    resolve_entities=False,
    no_network=True,
    load_dtd=False,
    huge_tree=False,
)
root = etree.fromstring(untrusted_xml, parser)
# Do not run XInclude on untrusted trees without a restricted loader.

Escolha configurações seguras para o parser implantado

No defusedxml.ElementTree, declarações de entidades e referências externas são bloqueadas por padrão; defina forbid_dtd=True também quando o formato aceito não precisar de DTD. No lxml, defina explicitamente resolve_entities=False, load_dtd=False, no_network=True e huge_tree=False. As versões atuais usam resolução apenas interna por padrão; antes do lxml 5.0, a resolução vinha ativada. Não processe XInclude em árvores não confiáveis sem um loader restrito. Limite o tamanho antes de descompactar e analisar; flags não limitam todos os custos.

Por que revisar padrões conforme a versão do parser

“XML em Python” não é uma única configuração. A biblioteca padrão usa Expat, incluído ou do sistema conforme a build do interpretador. lxml usa libxml2 e mudou o padrão de resolução de entidades na versão 5.0; wrappers e opções explícitas podem mudar o comportamento. Registre Python, Expat, lxml e libxml2 no inventário e declare as flags em cada ponto que aceita entrada não confiável.

Perguntas frequentes

ElementTree lê arquivos locais via entidades externas por padrão?

Não. A documentação do Python diz que Expat não acessa arquivos locais nem abre conexões de rede por padrão. Confira a versão do Expat e diferencie acesso a entidades externas de riscos de exaustão em versões antigas.

O lxml atual é inseguro por padrão?

Não generalize entre versões. O XMLParser atual do lxml usa resolução apenas interna, não carrega DTDs e não acessa a rede por padrão. O padrão de resolve_entities mudou de True para internal no lxml 5.0. Defina as opções e revise wrappers.

defusedxml rejeita toda DTD por padrão?

Não. Os padrões bloqueiam declarações de entidade e referências externas, mas forbid_dtd é False. Use forbid_dtd=True se o formato não precisar de DTD.

Um parser seguro elimina todos os riscos de recursos XML?

Não. Limite o conteúdo comprimido e expandido, mantenha parsers atualizados, evite huge_tree e não execute XInclude não confiável com loader irrestrito.

Referências primárias

Veja também Injeção de entidade externa XML (XXE, CWE-611): como funciona e como evitar — flawopen.com XmlReader do .NET é seguro contra XXE? XXE em Java: DocumentBuilderFactory é segura por padrão?