Xml External Entities in Grape with Basic Auth
Xml External Entities in Grape with Basic Auth — how this specific combination creates or exposes the vulnerability
XML External Entity (XXE) injection occurs when an application processes XML input that references external entities, allowing an attacker to force the parser to read local files, trigger SSRF, or consume excessive resources. Grape is a REST-like API micro-framework for Ruby, and it often receives XML payloads when clients send request bodies with Content-Type application/xml. When Grape parses XML using libraries such as Nokogiri without disabling external entity processing, the parser can be tricked into loading local files or remote URLs via DOCTYPE declarations.
Combining Basic Auth with XXE introduces two important dynamics. First, authentication via HTTP Basic Auth typically relies on the Authorization header (e.g., Authorization: Basic base64(username:password)). If your Grape routes expect credentials in headers and also parse XML from the request body, you might inadvertently trust XML data that carries attacker-controlled entity definitions. Second, attackers can chain authentication bypass or information disclosure: they may probe whether authenticated routes still parse XML unsafely, or they might attempt to exfiltrate credentials or configuration data through external entity reads before authentication enforcement is fully applied.
A concrete scenario: a Grape API endpoint accepts XML to configure a resource. The route uses Basic Auth to validate a token in the header, then passes the XML body to Nokogiri::XML. If the parser resolves external entities, an attacker can supply a DOCTYPE like ]>. The parser fetches and includes /etc/passwd in the response, revealing sensitive data. Even when Grape enforces Basic Auth, the XML parser runs with the same permissions as the application, so file reads can expose environment variables or source code snippets. Because Grape can be configured with different parsers and middleware, the exact behavior depends on how you initialize XML parsing, but by default many Ruby XML libraries are vulnerable if not explicitly hardened.
To detect this in practice, you can use an unauthenticated scan (since middleBrick tests the unauthenticated attack surface by default) or include credentials in the Authorization header while sending malicious XML. A scan will flag whether XML parsing allows entity expansion, file reads, or SSRF. The presence of Basic Auth does not inherently mitigate XXE; it only controls access to the endpoint. If the parser processes external entities, the vulnerability remains regardless of whether credentials are valid. Therefore, you must disable external entity loading in the XML parser and ensure that authentication checks occur before any potentially malicious XML is processed.
Basic Auth-Specific Remediation in Grape — concrete code fixes
Remediation focuses on two layers: secure authentication in Grape and safe XML parsing. For authentication, prefer token-based approaches or at least ensure credentials are validated before any XML processing. For XML, disable external entities and DTDs in your parser. Below are concrete, working examples for Grape with Basic Auth and hardened XML parsing.
1) Grape with HTTP Basic Auth and safe XML parsing using Nokogiri (with external entities disabled):
require 'grape'
require 'nokogiri'
class MyAPI < Grape::API
format :xml
before do
# Perform Basic Auth validation before parsing request body
auth = Rack::Auth::Basic::Request.new(env)
unless auth.provided? && auth.basic? && auth.credentials && auth.credentials == [ENV['API_USER'], ENV['API_PASS']]
error!('Unauthorized', 401)
end
end
resource :items do
desc 'Create an item with XML payload'
params do
requires :name, type: String, desc: 'Item name'
end
post do
# After authentication, parse XML safely
doc = Nokogiri::XML(request.body.read, nil, 'UTF-8', options: Nokogiri::XML::ParseOptions::NOENT << Nokogiri::XML::ParseOptions::NONET << Nokogiri::XML::ParseOptions::STRICT)
# Ensure no external entities are expanded
doc.external_subset = nil
name = doc.at_xpath('//name')&.text
{ name: name }
end
end
end
Key points in the code: Authentication runs in a before block using Rack::Auth::Basic::Request, so credentials are checked before any XML parsing. When parsing, we pass options NOENT (disable parameter entities), NONET (disable network access), and STRICT, and explicitly set doc.external_subset = nil to prevent external entity resolution. This combination closes the XXE vector while still allowing legitimate XML processing.
2) Alternative with Ox parser (faster, requires explicit configuration to avoid expansion):
require 'grape'
require 'ox'
class MyAPI < Grape::API
format :xml
before do
auth = Rack::Auth::Basic::Request.new(env)
unless auth.provided? && auth.basic? && auth.credentials && auth.credentials == [ENV['API_USER'], ENV['API_PASS']]
error!('Unauthorized', 401)
end
end
resource :items do
desc 'Create an item with XML payload using Ox'
params do
requires :data, type: String, desc: 'XML string'
end
post do
# Use Ox with mode :safe to avoid external entities
parsed = Ox.parse(params[:data], mode: :safe)
# Extract data safely; Ox does not expand external entities in safe mode
name = parsed.locate('//name').first&.value
{ name: name }
end
end
end
Using Ox with mode: safe disables DTD and external entity processing by default, which is appropriate for untrusted XML. As with the Nokogiri example, authentication with Basic Auth is enforced before parsing, ensuring that only authorized requests proceed to XML handling.
3) Defense in depth: If you accept XML, consider validating against a strict schema (XSD) and avoid passing raw XML to parsers that might be misconfigured. Also, prefer JSON for new APIs, as it avoids XML-specific risks entirely. middleBrick scans can help identify whether your Grape endpoints parse XML unsafely; the CLI (middlebrick scan