Crlf Injection in Grape with Firestore
Crlf Injection in Grape with Firestore — how this specific combination creates or exposes the vulnerability
Crlf Injection occurs when an attacker can inject carriage return (CR, \r) and line feed (\n) characters into an HTTP header or a value later reflected into headers. In a Grape API that uses Firestore as a data store, this typically happens when developer-controlled input (e.g., a document field such as displayName or a query parameter like docName) is written to Firestore and later read and used in an HTTP response header without sanitization.
Consider a Grape endpoint that retrieves a Firestore document and uses a field in a Location header for a redirect:
# BAD: directly using Firestore document data in a header
class RedirectResource < Grape::API
format :json
helpers do
def firestore_client
# Assume Firestore client initialized elsewhere
end
end
get '/redirect/:docId' do
doc_ref = firestore_client.doc("pages/#{params[:docId]}")
doc = doc_ref.get
raise Grape::Errors::NotFound unless doc.exists?
# Risk: if doc.data['location'] contains \r\n, response headers can be split
header 'Location', doc.data['location']
status 302
{}
end
end
If a Firestore document contains location: "https://example.com\r\nSet-Cookie: session=attacker", the response will include an extra header, enabling session fixation or cache poisoning. Firestore itself is only a data source here; the vulnerability is introduced by Grape reflecting untrusted data into HTTP headers without validation. Attack patterns such as response splitting can manipulate caching proxies or browsers, and may facilitate cross-site scripting via injected headers when combined with other weaknesses.
Another scenario involves query parameters derived from Firestore documents being reflected in headers. For example, an API that returns a custom header like X-Document-Name using a Firestore field value can be exploited if that field contains CRLF sequences. Because the API does not treat Firestore data as inherently safe, sanitization must be applied before any use in headers. The risk is compounded when logs or error messages also echo these values, providing additional injection surfaces.
Middleware or infrastructure layers do not neutralize this risk; validation must happen in the application logic. The core issue is treating data from Firestore as safe for header output without canonicalization. Attackers commonly probe for such patterns using newline characters to break header structure and inject additional headers or response bodies, undermining the integrity of the HTTP transaction.
Firestore-Specific Remediation in Grape — concrete code fixes
Remediation centers on strict input validation and output encoding when using Firestore data in HTTP headers. Never directly reflect Firestore field values into headers. Instead, enforce a strict allowlist for values that can be used in headers such as Location, and sanitize any data that may be stored or displayed.
Safe implementation example with explicit validation and sanitization:
class SafeRedirectResource < Grape::API
format :json
helpers do
def firestore_client
# Assume Firestore client initialized elsewhere
end
def sanitized_location(input)
# Remove or replace CR and LF characters; restrict to a safe subset
input.to_s.gsub(/[\r\n]/, '')
end
def valid_location_url?(url)
# Basic validation: must be http/https and no newlines remain
uri = URI.parse(url)
%w[http https].include?(uri.scheme) && uri.host.present?
rescue URI::InvalidURIError
false
end
end
get '/redirect/:docId' do
doc_ref = firestore_client.doc("pages/#{params[:docId]}")
doc = doc_ref.get
raise Grape::Errors::NotFound unless doc.exists?
raw_location = doc.data['location']
safe_location = sanitized_location(raw_location)
unless valid_location_url?(safe_location)
throw(:error, message: 'Invalid location', status: 400)
end
# Safe: sanitized value is free of CR/LF
header 'Location', safe_location
status 302
{}
end
end
For non-header uses, such as rendering user-supplied content in JSON responses, apply context-aware escaping and avoid embedding newlines in values that might be concatenated into headers or logs. If Firestore stores metadata that may be displayed in UI headers, normalize line endings and enforce length limits to reduce impact.
Additionally, consider schema validation when writing to Firestore to prevent storage of malformed data. For instance, validate that fields like location conform to expected formats before insertion:
# During data ingestion
location = params['location']
if location.to_s.match?(URI::DEFAULT_PARSER.make_regexp(%w[http https])) && !location.include?("\n") && !location.include?("\r")
firestore_client.doc("pages/#{docId}").set({ location: location })
else
# reject or sanitize
end
These practices ensure that data from Firestore does not compromise header integrity in Grape APIs, mitigating Crlf Injection while preserving functionality.