Xpath Injection in Fastapi
How Xpath Injection Manifests in Fastapi
XPath injection in FastAPI applications typically occurs when user-supplied input is directly incorporated into XPath queries without proper sanitization. This vulnerability is particularly dangerous in FastAPI applications that handle XML data or interact with XML-based APIs.
Consider a FastAPI endpoint that processes XML data and uses XPath to query specific elements. If user input is concatenated directly into the XPath expression, an attacker can manipulate the query structure. For example:
from fastapi import FastAPI, HTTPException
from lxml import etree
app = FastAPI()
def parse_xml(xml_data: str):
return etree.fromstring(xml_data)
@app.post("/search-users")
async def search_users(xml_data: str, username: str):
try:
root = parse_xml(xml_data)
# Vulnerable: direct string interpolation
xpath_query = f"//user[username='{username}']"
result = root.xpath(xpath_query)
return {"found": bool(result)}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))An attacker could submit a username like admin' or '1'='1, causing the XPath query to become:
//user[username='admin' or '1'='1']This would return all user elements, bypassing authentication. More sophisticated attacks can use XPath functions like contains(), starts-with(), or even attempt to read arbitrary XML files if the application has file access permissions.
FastAPI's dependency injection system can inadvertently create XPath injection points when XML processing is abstracted. For instance, a dependency that parses XML and extracts data using unsanitized XPath expressions:
from fastapi import Depends
def xml_parser(xml_data: str, xpath_expr: str = Depends(get_xpath_expr)):
root = etree.fromstring(xml_data)
return root.xpath(xpath_expr) # Vulnerable if xpath_expr is user-controlledThe vulnerability becomes more severe when combined with XML External Entity (XXE) attacks, as XPath injection can be used to extract data from external entities or cause denial of service through resource-intensive queries.
FastAPI-Specific Detection
Detecting XPath injection in FastAPI applications requires both static code analysis and dynamic testing. Static analysis tools can identify dangerous patterns where user input is directly interpolated into XPath expressions. Look for:
- String formatting operations (
f"...{variable}...") with XPath context - XPath method calls with concatenated strings
- XML parsing followed by XPath evaluation without input validation
Dynamic testing with middleBrick can automatically detect XPath injection vulnerabilities in your FastAPI endpoints. The scanner tests for injection patterns by submitting specially crafted payloads to parameters that might be used in XPath expressions:
middlebrick scan https://your-fastapi-app.com/api/search-users \
--method POST \
--body '{"xml_data": "admin ", "username": "admin' or '1'='1"}'middleBrick's XPath injection detection specifically looks for:
- Boolean logic injection (
or '1'='1',and '1'='0') - XPath function injection (
contains(),starts-with(),substring()) - Comment injection (
--]) - Union injection attempts
The scanner also tests for related XML vulnerabilities that often accompany XPath injection, such as XML External Entity (XXE) processing and XML bomb attacks. For FastAPI applications, middleBrick can scan both the OpenAPI specification and runtime endpoints to identify potential injection points.
During scanning, middleBrick evaluates the application's response to malicious inputs. If the application returns different results for benign versus malicious inputs, this indicates a potential XPath injection vulnerability. The scanner categorizes findings by severity and provides specific remediation guidance for FastAPI applications.
FastAPI-Specific Remediation
Remediating XPath injection in FastAPI requires a defense-in-depth approach. The primary mitigation is to use parameterized XPath queries instead of string concatenation. While XPath doesn't have native parameter binding like SQL, you can use lxml's XPath class with namespaces:
from lxml import etree
from fastapi import FastAPI, HTTPException
app = FastAPI()
def parse_xml(xml_data: str):
return etree.fromstring(xml_data)
@app.post("/search-users-safe")
async def search_users_safe(xml_data: str, username: str):
try:
root = parse_xml(xml_data)
# Use XPath with proper escaping
xpath_expr = etree.XPath(
"//user[username=$username]",
namespaces={'ns': 'http://example.com/ns'}
)
result = xpath_expr(root, username=username)
return {"found": bool(result)}
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))For applications that must use dynamic XPath expressions, implement strict input validation and whitelisting. Only allow specific characters and patterns:
import re
from fastapi import FastAPI, HTTPException
app = FastAPI()
def validate_xpath_input(value: str) -> str:
# Allow only alphanumeric, underscores, and hyphens
if not re.match(r'^[a-zA-Z0-9_-]+$', value):
raise HTTPException(
status_code=400,
detail="Invalid characters in input"
)
return value
@app.post("/search-users-validated")
async def search_users_validated(xml_data: str, username: str):
username = validate_xpath_input(username)
root = parse_xml(xml_data)
xpath_query = f"//user[username='{username}']"
result = root.xpath(xpath_query)
return {"found": bool(result)}Another effective approach is to use XML schema validation before processing. Define an XML schema that restricts the structure and content of incoming XML data, preventing malicious payloads:
from lxml import etree, xmlschema
# Define XML schema
XML_SCHEMA = '''
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="users">
<xs:complexType>
<xs:sequence>
<xs:element name="user" maxOccurs="unbounded">
<xs:complexType>
<xs:sequence>
<xs:element name="username" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
'''
schema = xmlschema.XMLSchema(XML_SCHEMA)
def validate_xml(xml_data: str):
try:
schema.validate(xml_data)
except xmlschema.XMLSchemaValidationError as e:
raise HTTPException(status_code=400, detail=f"Invalid XML: {e}")FastAPI's dependency injection can be leveraged to centralize XML validation and XPath query construction, ensuring consistent security controls across all endpoints:
from fastapi import Depends
def secure_xml_processor(xml_data: str = Depends(validate_xml)):
root = etree.fromstring(xml_data)
return root
@app.post("/search-users-secure")
async def search_users_secure(
xml_data: str,
username: str,
processor: etree._Element = Depends(secure_xml_processor)
):
# Safe processing using validated XML
xpath_query = f"//user[username='{username}']"
result = processor.xpath(xpath_query)
return {"found": bool(result)}Frequently Asked Questions
How does XPath injection differ from SQL injection in FastAPI applications?
' OR '1'='1 while XPath uses similar boolean logic but within XML context. FastAPI applications handling XML APIs, configuration files, or document processing are more susceptible to XPath injection than those primarily dealing with databases.