Xml External Entities in Rails with Jwt Tokens
Xml External Entities in Rails with Jwt Tokens — 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 read local files, perform SSRF, or cause denial of service. In Rails, this risk can intersect with JWT token handling when XML parsing is used in the request/response lifecycle and JWT tokens are involved either as data within XML payloads or as bearer tokens that gate XML-processing endpoints.
Consider a Rails API that accepts an XML document containing sensitive data and also requires a JWT token for authorization. If the controller parses the XML using a vulnerable Ruby XML parser (such as the default Nokogiri with unsafe entity expansion enabled), an attacker-supplied external entity can read files like /etc/passwd or trigger SSRF to internal metadata services. Meanwhile, the JWT token may be transmitted in an Authorization: Bearer <token> header. While the token itself is not directly used in XML parsing, the presence of a valid JWT can authenticate the request and grant the attacker a higher-risk context—such as access to an XML ingestion endpoint that otherwise requires authentication. This combination means an authenticated request (via JWT) can be weaponized to exploit XXE, exposing secrets that would otherwise be protected by authentication alone.
Another scenario involves JWT claims embedded inside XML payloads. If a Rails app deserializes an XML body that includes claims copied from a JWT (e.g., user roles or permissions) without proper validation, malicious entity definitions can manipulate the document structure and exfiltrate data. Since the JWT provides the identity context, the resulting XXE attack can be tied to a specific user, making detection and auditing more complex.
Real-world exploit patterns include crafted XML bodies with declarations like <!DOCTYPE foo [ <!ENTITY xxe SYSTEM "file:///etc/passwd" >]> and external DTDs that reference internal URLs. In Rails logs, such requests may appear as normal authenticated API calls, but the backend XML parser resolves the external references, leading to unintended data exposure. Because the scan surface often includes unauthentinated attack paths, middleBrick can detect XXE indicators even when JWT tokens are not required for the tested endpoint; however, when JWT-protected endpoints are in scope, authenticated scans increase the likelihood of triggering authenticated-sensitive flows.
To summarize, the intersection of XXE and JWT in Rails arises when (1) XML parsing is enabled in the app and (2) JWT tokens are used for authentication or identity claims. The token does not cause the XXE, but it can enable more severe impact by allowing the attacker to target authenticated endpoints and access protected resources through a vulnerable XML parser.
Jwt Tokens-Specific Remediation in Rails — concrete code fixes
Remediation focuses on disabling external entity processing in XML parsers and ensuring JWT handling does not inadvertently feed untrusted data into XML workflows. Below are concrete, applicable code examples for a Rails application.
1. Secure XML parsing with Nokogiri
Always configure Nokogiri to disallow external entities. Avoid the default legacy behavior that permits entity expansion.
# app/lib/safe_xml.rb
module SafeXml
def self.parse(xml_string)
# Disable DOCTYPE and external entities
parser = Nokogiri::XML::SAX::Parser.new
Nokogiri::XML(xml_string) do |config|
config.options = Nokogiri::XML::ParseOptions::STRICT | Nokogiri::XML::ParseOptions::NOENT
# NOENT replaces entities, preventing external expansion
end
rescue => e
Rails.logger.error("XML parse error: #{e.message}")
raise ActionController::BadRequest, "Invalid XML"
end
end
Use this parser in controllers instead of raw Nokogiri::XML(params[:xml]).
2. Avoid XML parsing for JSON-first APIs
If your API primarily uses JSON, disable XML parsing entirely and validate content type to reduce attack surface.
# app/controllers/application_controller.rb
before_action :require_json_content_type, if: :xml_parsing_disabled?
private
def require_json_content_type
unless request.content_type == "application/json"
render json: { error: "Unsupported Media Type" }, status: :unsupported_media_type
end
end
3. JWT handling: verify and limit claims before use
Always verify JWTs with a strong algorithm and validate claims before using them in any downstream processing, including building XML payloads.
# app/services/jwt_decoder.rb
class JwtDecoder
ALGORITHM = 'HS256'
def initialize(token, secret)
@token = token
@secret = secret
end
def call
decoded = JWT.decode(@token, @secret, true, { algorithm: ALGORITHM })
payload = decoded.first
validate_claims(payload)
payload
end
private
def validate_claims(payload)
raise JWT::DecodeError, 'Missing required claim' unless payload['sub']
# Ensure iss and exp are validated by JWT library; add custom checks as needed
end
end
In your controller, use the decoder and avoid passing raw or merged XML data that could contain injected entities.
# app/controllers/api/xml_controller.rb
class Api::XmlController < ApplicationController
skip_before_action :verify_authenticity_token
def create
token = request.headers['Authorization']&.split(' ')&.last
decoded = JwtDecoder.new(token, Rails.application.credentials.secret_key_base).call
# Use decoded identity safely; do not embed untrusted XML fragments into JWT-derived structures
xml_data = SafeXml.parse(request.body.read)
# Process xml_data with decoded identity as needed
render json: { status: 'ok', user: decoded['sub'] }
end
end
4. Enforce least privilege and input validation
Treat JWT identity as authorization context, but do not rely on it to sanitize XML. Validate and whitelist expected XML structures and reject unexpected entities or external references.
# Example schema-based validation (using oxforde/oga gem or similar)
# Reject DOCTYPE and external references at the parser level
# In tests, assert that parsed XML does not contain entity expansions
5. Continuous monitoring via scans
Use middleBrick to validate that XXE indicators are not present in your exposed endpoints. The CLI can be integrated into scripts and pipelines. For ongoing assurance, the Pro plan provides continuous monitoring so that new XML-related findings are surfaced promptly.