Xml External Entities in Rails with Mongodb
Xml External Entities in Rails with Mongodb — how this specific combination creates or exposes the vulnerability
XML External Entity (XXE) injection occurs when an application parses XML input that references external entities, allowing an attacker to force the parser to disclose local files, trigger SSRF, or consume resources. In a Ruby on Rails stack using MongoDB as the primary database, the risk arises not from MongoDB itself, but from libraries used to process XML before data is handled or stored. Rails applications often rely on gems such as nokogiri for XML parsing. If these libraries are configured to resolve external entities, an attacker can supply a malicious XML payload that reads files like /etc/passwd or interacts with internal services reachable from the server.
Consider a Rails controller that accepts an XML upload to create a user profile and stores metadata in MongoDB:
class ProfilesController < ApplicationController
def create
xml_data = request.body.read
doc = Nokogiri::XML(xml_data)
username = doc.at_css('username')&.text
bio = doc.at_css('bio')&;.text
User.create!(username: username, bio: bio) # stores in MongoDB
render json: { status: 'ok' }
end
end
If Nokogiri is not configured to disable external entity resolution, an attacker can submit an XML payload that defines an external entity pointing to a sensitive local file. Upon parsing, the entity expands to the file contents, which may be included in the parsed document and inadvertently logged, echoed, or stored in MongoDB fields. Even though MongoDB does not process XML, the application layer deserialization step is the attack surface. A crafted payload can also leverage parameter entities to trigger SSRF against internal endpoints, exposing internal MongoDB connections or admin interfaces that are bound to localhost.
Another scenario involves background processing jobs that parse XML reports stored in GridFS or received from third-party systems. If the job uses an XML parser without disabling DTD loading and external entity resolution, the worker can be tricked into accessing internal metadata services or cloud instance metadata endpoints. Because Rails applications often integrate multiple libraries for serialization and deserialization, it is crucial to audit each XML-handling component rather than assuming MongoDB storage negates the risk.
Mongodb-Specific Remediation in Rails — concrete code fixes
Mitigating XXE in Rails applications that use MongoDB centers on ensuring XML parsing is performed in a safe, non-expansive mode. You should configure Nokogiri (or any XML library) to explicitly disable external entities and DTDs. Below are concrete, safe patterns for common Rails workflows.
1. Safe Nokogiri parsing in controllers and services
When parsing user-supplied XML, avoid the default behavior and instead use Nokogiri::XML::Document.parse with options that disable external subsets and network access:
def safe_parse(xml_string)
# Disable external entities and DTDs
Nokogiri::XML(xml_string) do |config|
config.options = Nokogiri::XML::ParseOptions::NONET | Nokogiri::XML::ParseOptions::NOENT
end
end
class ProfilesController < ApplicationController
def create
xml_data = request.body.read
doc = safe_parse(xml_data)
username = doc.at_css('username')&.text
bio = doc.at_css('bio')&;.text
# Store in MongoDB using Mongoid or Mongo Ruby driver
User.create!(username: username, bio: bio)
render json: { status: 'ok' }
end
end
2. Using the mongo gem directly with sanitized data
If you use the mongo gem to insert documents into MongoDB, ensure that the data fed into the insert operation has already been processed safely:
client = Mongo::Client.new(['127.0.0.1:27017'], database: 'myapp')
collection = client[:profiles]
# Assume safe_parse was used above
parsed = safe_parse(request.body.read)
payload = {
username: parsed.at_css('username')&.text,
bio: parsed.at_css('bio')&;.text,
created_at: Time.now.utc
}
result = collection.insert_one(payload)
puts "Inserted document ID: #{result.inserted_id}"
3. Disabling XML parsing entirely when unnecessary
If your application does not require XML, remove or restrict XML parsing libraries and routes that accept XML. In Rails, you can explicitly limit request formats:
class Api::V1::ProfilesController < ApplicationController
before_action :ensure_json_format, only: [:create, :update]
private
def ensure_json_format
unless request.format.json?
render json: { error: 'Unsupported Media Type' }, status: :unsupported_media_type
end
end
end
4. Validating and sanitizing stored content
Even after safe parsing, validate and sanitize fields that may be rendered later to prevent injection into downstream systems. Use Rails validators and store only the necessary data in MongoDB fields:
class User
include Mongoid::Document
field :username, type: String
field :bio, type: String
validates :username, presence: true, length: { maximum: 50 }
validates :bio, presence: false, length: { maximum: 500 }
end
5. Monitoring and dependency hygiene
Regularly audit your Gemfile for older versions of nokogiri or other XML libraries that may default to unsafe parsing. Prefer explicit configuration and update dependencies to versions that have addressed known parser vulnerabilities. Combine this with runtime scanning using a tool that tests unauthenticated attack surfaces to detect unexpected XML processing behavior.