Crlf Injection in Grape
How Crlf Injection Manifests in Grape
CRLF injection in Grape APIs occurs when untrusted input containing carriage return (\r) or line feed (\n) characters is used in HTTP response headers without proper sanitization. This vulnerability allows attackers to inject arbitrary HTTP headers or split responses, potentially leading to HTTP response splitting, session fixation, cross-site scripting, or cache poisoning.
In Grape applications, CRLF injection commonly appears in these contexts:
- Response headers: Custom headers generated from user input, such as X-Custom-Header: <%= params[:value] %>
- Location redirects: Redirect URLs constructed from query parameters without validation
- Set-Cookie headers: Cookie values containing CRLF sequences
- Custom status messages: HTTP status descriptions built from user data
Here's a vulnerable Grape endpoint that demonstrates the issue:
class VulnerableAPI < Grape::API
get :redirect do
# DANGEROUS: User input directly in Location header
redirect_url = params[:url] || '/default'
redirect redirect_url
end
get :custom_header do
# DANGEROUS: Header injection possible
header_value = params[:value] || 'default'
header 'X-Custom-Header', header_value
{ message: 'Header set' }
end
get :set_cookie do
# DANGEROUS: Cookie injection possible
cookie_value = params[:value] || 'default'
cookie 'session_id', cookie_value
end
endAn attacker could exploit the redirect endpoint with: /redirect?url=http://evil.com%0D%0ASet-Cookie:%20session=malicious, which would inject a Set-Cookie header into the response.
Grape-Specific Detection
Detecting CRLF injection in Grape APIs requires both static code analysis and runtime scanning. Here are Grape-specific detection strategies:
Static Analysis: Review code for patterns where user input flows into response headers. Look for:
- Direct parameter usage in
redirect,header, orcookiemethods - String interpolation in header values without sanitization
- Dynamic header construction based on user input
Runtime Scanning with middleBrick: middleBrick's black-box scanning approach is particularly effective for CRLF injection because it:
- Tests endpoints with CRLF payloads (
%0D%0Afor URL encoding) - Analyzes response headers for unexpected header injection
- Checks for HTTP response splitting by sending multiple response lines
- Verifies proper header encoding and validation
middleBrick's CRLF Detection includes:
CRLF Injection Scan Results:
- Test: Header Injection (X-Test-Header)
Payload: %0D%0AX-Test-Header:%20injected
Result: No injection detected ✓
- Test: Response Splitting
Payload: %0D%0AContent-Type:%20text/html%0D%0A%0D%0A<script>alert(1)</script>
Result: No response splitting detected ✓
- Test: Cookie Injection
Payload: %0D%0ASet-Cookie:%20malicious=value
Result: No cookie injection detected ✓middleBrick also scans OpenAPI specifications for header definitions that might accept unsafe input and cross-references these with runtime findings.
Grape-Specific Remediation
Securing Grape APIs against CRLF injection requires input validation and proper header handling. Here are specific remediation strategies:
Input Sanitization: Use Grape's built-in parameter validation to whitelist acceptable characters:
class SecureAPI < Grape::API
params do
requires :url, type: String, allow_blank: false,
regexp: //(https?://)?[\-\w]+(\.\w+)+([/?#][^\s]*)?/i
requires :value, type: String, allow_blank: false,
regexp: /^[^\r\n]*$/ # No CRLF characters
end
get :redirect do
redirect params[:url]
end
params do
requires :value, type: String,
regexp: /^[^\r\n]*$/ # Block CRLF
end
get :custom_header do
header 'X-Custom-Header', params[:value]
{ message: 'Header set' }
end
endSafe Header Construction: Use Grape's safe header methods and validate input:
class SecureAPI < Grape::API
helpers do
def safe_redirect(url)
# Validate URL format and sanitize
sanitized_url = URI.parse(url).normalize.to_s
# Check for CRLF injection
raise 'Invalid URL' if sanitized_url.include?("\r") || sanitized_url.include?("\n")
redirect sanitized_url
end
def safe_set_cookie(name, value)
# Remove any CRLF characters
sanitized_value = value.gsub(/[
]/, '')
cookie name, sanitized_value
end
end
get :redirect do
safe_redirect(params[:url] || '/default')
end
get :set_cookie do
safe_set_cookie('session_id', params[:value] || 'default')
end
endHeader Whitelisting: Only allow specific, known-safe headers:
class SecureAPI < Grape::API
ALLOWED_HEADERS = %w[Content-Type Cache-Control X-Custom-Header].freeze
get :custom_header do
header_name = params[:header_name]
header_value = params[:header_value]
if ALLOWED_HEADERS.include?(header_name)
# Sanitize value
sanitized_value = header_value.gsub(/[
]/, '')
header header_name, sanitized_value
{ message: 'Header set' }
else
error!('Invalid header', 400)
end
end
endContent Security Policy: Add CSP headers to mitigate XSS from successful CRLF attacks:
class SecureAPI < Grape::API
before do
header 'Content-Security-Policy', "default-src 'self'; script-src 'self'"
end
end