Injection Flaws in Rails with Mutual Tls
Injection Flaws in Rails with Mutual Tls
In Rails applications that enforce mutual TLS (mTLS), developers sometimes assume the transport-layer guarantees are sufficient to prevent injection attacks. This is not the case. mTLS provides strong peer authentication and encryption, but it does not alter how Rails parses and uses request data. Injection flaws—such as SQL injection, command injection, and template injection—occur when untrusted input is concatenated into system commands or queries without proper sanitization or parameterization. mTLS does not prevent an authenticated client from sending malicious payloads in headers, cookies, or request bodies that exploit these coding errors.
Consider a controller that uses a client certificate subject to build a query:
# app/controllers/reports_controller.rb
class ReportsController < ApplicationController
# mTLS ensures client_cert is present, but does not sanitize its content
def show
client_name = request.client_cert.subject.match(/CN=([^,]+)/)&.[](1)
# Risk: SQL injection if client_name is interpolated
@data = Report.where("client_name = '#{client_name}'")
end
end
Even with mTLS, an authenticated client could present a certificate with a subject like /CN=attacker' OR '1'='1. Because the query uses string interpolation, the malicious value alters SQL logic. mTLS ensures the connection is authenticated, but it does not validate or escape the content of that authentication.
Another scenario involves system command execution. Suppose a Rails app uses certificate common name to construct a shell command for diagnostics:
# app/services/network_diag.rb
client_cn = request.client_cert.subject.match(/CN=([^,]+)/)&.[](1)
# Risk: Command injection via client_cn
output = `ping -c 1 #{client_cn}`
If a client presents /CN=localhost; cat /etc/passwd, the command injection succeeds regardless of mTLS. The transport security does not constrain how the application uses the identity metadata.
Template injection is also possible. If a certificate attribute is passed directly to a Rails view without escaping, an attacker can achieve reflected code execution in the rendering context:
# app/views/reports/show.html.erb
<%= raw @client_cn %>