FlawOpen

flawopen.com/Entidades externas XML (XXE)/Seguridad de parsers XML en Python

¿Es seguro contra XXE xml.etree.ElementTree de Python?

CWE-611: Referencia a Entidades Externas XMLPágina de referencia
Respuesta breve

Para el XXE clásico de archivos y red, los parsers integrados de Python basados en Expat no acceden a archivos locales ni abren conexiones de red de forma predeterminada. Eso no hace seguro todo uso de XML: Expat anterior a 2.7.2 puede sufrir DoS por expansión de entidades y tokens largos. Los valores predeterminados de lxml varían según la versión. Fija y configura explícitamente los parsers o usa defusedxml con XML no confiable.

RIESGOSO — la versión y las opciones del parser cambian la exposición

XXE (CWE-611):
El parser resuelve una entidad externa de XML controlado por el atacante y puede leer archivos locales o hacer solicitudes desde el servidor.
DoS por expansión de entidades:
Referencias anidadas o repetidas consumen CPU o memoria desproporcionadas al expandirse.
Expat:
Parser C de los módulos XML integrados de Python; comprueba la versión activa con pyexpat.EXPAT_VERSION.
DTD:
Una declaración de tipo de documento puede definir entidades y, según la configuración, solicitar recursos externos.
Paso 1

XML no confiable llega al parser

Una carga, SVG, mensaje SAML o dato de integración cruza el límite de confianza y llega a una API XML.

Paso 2

La implementación y las opciones determinan el comportamiento

La biblioteca estándar usa Expat; lxml usa libxml2. Versión, resolución externa, expansión y flags definen lo que puede ocurrir.

Paso 3

La resolución o expansión produce un efecto

Si se habilita la resolución externa, una entidad puede pedir contenido local o remoto. La expansión vulnerable o los tokens largos pueden agotar recursos.

Paso 4

Los permisos del proceso limitan el impacto

Permisos de archivos y salida de red acotan el XXE; límites de entrada, parsers actualizados, flags seguras y aislamiento reducen la exposición.

La guía actual de Python indica que Expat no accede a archivos locales ni crea conexiones de red de forma predeterminada. También advierte que versiones anteriores a 2.7.2 pueden ser vulnerables a billion laughs, quadratic blowup, tokens largos o consumo desproporcionado de memoria; comprueba pyexpat.EXPAT_VERSION, pues Python puede usar Expat incluido o del sistema. Es un riesgo de agotamiento según la versión, no una prueba de que ElementTree actual resuelva entidades externas.

ENDURECIDO — rechaza DTD y 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)
Tres riesgos distintos de los 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.

Elige opciones seguras para el parser desplegado

En defusedxml.ElementTree, las declaraciones de entidades y referencias externas se prohíben de forma predeterminada; añade forbid_dtd=True si el formato aceptado no necesita DTD. En lxml, define explícitamente resolve_entities=False, load_dtd=False, no_network=True y huge_tree=False. Las versiones actuales solo resuelven entidades internas por defecto; antes de lxml 5.0 se resolvían. No ejecutes XInclude en árboles no confiables sin un loader restringido. Limita el tamaño antes de descomprimir y analizar: las flags no acotan todos los costes.

Por qué revisar los valores del parser según la versión

“XML de Python” no es una configuración única. La biblioteca estándar usa Expat, incluido o del sistema según la compilación del intérprete. lxml envuelve libxml2 y cambió el valor predeterminado de resolución de entidades en 5.0; wrappers y opciones pueden cambiarlo otra vez. Registra Python, Expat, lxml y libxml2, y declara las flags en cada punto de entrada no confiable.

Preguntas frecuentes

¿ElementTree lee archivos locales mediante entidades externas de forma predeterminada?

No. Python documenta que Expat no accede a archivos locales ni abre conexiones de red por defecto. Comprueba la versión y diferencia la lectura de entidades externas de los problemas de agotamiento en parsers antiguos.

¿lxml actual es inseguro por defecto?

No generalices entre versiones. El XMLParser actual de lxml resuelve solo entidades internas, no carga DTD y no usa la red de forma predeterminada. resolve_entities cambió de True a internal en lxml 5.0. Define las opciones explícitamente y revisa los wrappers.

¿defusedxml rechaza todas las DTD por defecto?

No. Sus valores predeterminados prohíben entidades y referencias externas, pero forbid_dtd es False. Pásalo a True si el formato no requiere DTD.

¿Un parser seguro elimina todos los riesgos de recursos XML?

No. Limita datos comprimidos y descomprimidos, actualiza los parsers, evita huge_tree y no procesa XInclude no confiable con un loader sin restricciones.

Referencias principales

Véase también Inyección de entidades externas XML (XXE, CWE-611): cómo funciona y cómo evitarla — flawopen.com ¿XmlReader de .NET es seguro frente a XXE? XXE en Java: ¿DocumentBuilderFactory es segura por defecto?