# XML External Entity (XXE) Injection

Language: Python
Severity: Critical
CWE: CWE-611

## Source
6

## Flow
6-7-8

## Sink
8

## Vulnerable Code
```python
from lxml import etree
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/iot/device/config', methods=['POST'])
def parse_device_telemetry():
    raw_xml = request.data
    parser = etree.XMLParser(resolve_entities=True, no_network=False)
    try:
        dev_tree = etree.fromstring(raw_xml, parser)
        sensor_id = dev_tree.findtext('.//sensorId')
        payload_val = dev_tree.findtext('.//reading')
        return jsonify({'sensor': sensor_id, 'value': payload_val})
    except etree.XMLSyntaxError as xe:
        return jsonify({'err': str(xe)}), 400
```

## Explanation

The endpoint reads raw XML from the HTTP request body (line 6) without any sanitization, then constructs an lxml XMLParser with resolve_entities=True and no_network=False (line 7), which explicitly enables external entity resolution and network access. When this misconfigured parser processes the untrusted XML via etree.fromstring() (line 8), an attacker can inject an XXE payload containing an external entity referencing local files (e.g., file:///etc/passwd) or internal network resources (SSRF), causing the server to disclose sensitive data.

## Remediation

The fix secures the lxml XMLParser by setting resolve_entities=False and no_network=True, which prevents the parser from resolving external (and internal) entity references and blocks any network access during parsing. Additionally, dtd_validation=False and load_dtd=False are set to prevent DTD loading entirely, providing defense-in-depth against all XXE attack vectors including file disclosure and SSRF.

## Secure Code
```python
from lxml import etree
from flask import Flask, request, jsonify
app = Flask(__name__)
@app.route('/iot/device/config', methods=['POST'])
def parse_device_telemetry():
    raw_xml = request.data
    parser = etree.XMLParser(resolve_entities=False, no_network=True, dtd_validation=False, load_dtd=False)
    try:
        dev_tree = etree.fromstring(raw_xml, parser)
        sensor_id = dev_tree.findtext('.//sensorId')
        payload_val = dev_tree.findtext('.//reading')
        return jsonify({'sensor': sensor_id, 'value': payload_val})
    except etree.XMLSyntaxError as xe:
        return jsonify({'err': str(xe)}), 400
```
