Command Injection in Phoenix with Mutual Tls
Command Injection in Phoenix with Mutual Tls — how this specific combination creates or exposes the vulnerability
Command Injection occurs when an attacker can inject and execute arbitrary system commands through an application. In a Phoenix application using Mutual TLS (mTLS), the risk can emerge not from the TLS layer itself, but from how the application uses client identity information obtained after mTLS authentication.
Mutual TLS verifies both the client and the server. Once the server has verified the client certificate, it often extracts details such as the Distinguished Name (DN), Common Name (CN), or Serial Number from the client certificate. If these values are used to construct system commands—such as logging commands, invoking external scripts, or calling binaries—and they are not properly sanitized, an attacker who presents a malicious certificate can inject shell metacharacters (e.g., ;, &, |, `, or $()) that lead to Command Injection.
For example, a developer might log certificate details using an OS command:
cmd = "openssl x509 -subject -in " & cert_path
System.cmd("sh", ["-c", cmd])
If cert_path is derived from user-controlled certificate fields and contains shell metacharacters, an attacker can break out of the intended command and execute arbitrary code. This is a classic OS Command Injection scenario (OWASP API Top 10 A03:2023). Even with mTLS ensuring only trusted clients connect, the application must treat extracted certificate attributes as untrusted input.
Another scenario involves authorization logic where the client CN is used to determine access rights and also passed to a shell command for reporting or filtering. An attacker with a valid certificate but malicious CN could exploit this to run unintended commands. middleBrick detects such patterns during scans, including checks related to Input Validation and Unsafe Consumption, and highlights Command Injection as a high-severity finding when detected.
Mutual Tls-Specific Remediation in Phoenix — concrete code fixes
Remediation focuses on never passing untrusted data—including certificate-derived fields—into system commands. Use structured data and safe libraries instead of shell commands, and strictly validate or encode any input that must be used in external calls.
1. Avoid Shell Commands Entirely
Replace OS command execution with native Erlang/Elixir libraries. For certificate parsing, use the public_key and ssl modules instead of invoking openssl via System.cmd.
# Unsafe: building a shell command with certificate-derived data
cert_path = "/tmp/certs/#{cn}.pem"
System.cmd("openssl", ["x509", "-subject", "-in", cert_path])
# Safe: use Erlang's public_key to parse the certificate
{:ok, cert_bin} = File.read(cert_path)
{:ok, cert} = :public_key.pkix_decode_cert(cert_bin, :otp)
subject = :public_key.pkix_verify_signature(cert) # example usage
# Process subject as structured data; no shell involved
2. Strict Input Validation and Encoding
If you must construct a command, validate and strictly encode all external inputs. Use allowlists for known-safe values and encode shell metacharacters. Prefer passing arguments as a list to System.cmd to avoid shell interpretation.
# Unsafe: direct interpolation
System.cmd("logger", ["User: #{user_cn}"])
# Safe: validate CN format and pass as argument without shell interpolation
if valid_cn?(user_cn) do
System.cmd("logger", ["User: " <> user_cn])
else
# handle invalid input
end
defp valid_cn?(cn) when is_binary(cn) do
# Allow only alphanumeric, hyphen, underscore, and dot
Regex.match?(~r/^[a-zA-Z0-9_.-]+$/, cn)
end
3. Principle of Least Privilege
Run the Phoenix server with minimal OS permissions. Even if injection occurs, this limits the impact. Combine mTLS client authorization with application-level role checks rather than relying on shell-level controls.
# config/config.exs
config :my_app, MyAppWeb.Endpoint,
http: [port: 4000],
transport_options: [socket_opts: [:binary, {:certfile, "path/to/server.pem"}, {:cacertfile, "path/to/ca.pem"}]]
# In your controller, after verifying mTLS, extract CN safely and enforce authorization
conn = fetch_certificate(conn)
cn = get_client_cn(conn)
if authorized_for?(cn, action) do
# proceed with business logic
else
send_resp(conn, 403, "Forbidden")
end
middleBrick scans can identify risky patterns such as shell command construction with certificate-derived inputs and map findings to frameworks like OWASP API Top 10 and SOC 2. The scans include checks for Authentication, Input Validation, and Unsafe Consumption, helping teams detect Command Injection risks in mTLS-enabled services.
Related CWEs: inputValidation
| CWE ID | Name | Severity |
|---|---|---|
| CWE-20 | Improper Input Validation | HIGH |
| CWE-22 | Path Traversal | HIGH |
| CWE-74 | Injection | CRITICAL |
| CWE-77 | Command Injection | CRITICAL |
| CWE-78 | OS Command Injection | CRITICAL |
| CWE-79 | Cross-site Scripting (XSS) | HIGH |
| CWE-89 | SQL Injection | CRITICAL |
| CWE-90 | LDAP Injection | HIGH |
| CWE-91 | XML Injection | HIGH |
| CWE-94 | Code Injection | CRITICAL |