Brute Force Attack in Chi with Mutual Tls
Brute Force Attack in Chi with Mutual Tls — how this specific combination creates or exposes the vulnerability
A brute force attack against an API using Mutual Transport Layer Security (mTLS) in a Chi-based Elixir service can be especially insidious because mTLS strongly authenticates the client, which may give operators a false sense of completeness. In Chi, this typically manifests when mTLS is enforced at the connection or plug level, but the application lacks additional protections around credential verification or request rate control. An attacker who has obtained a valid client certificate (for example, through theft, social engineering, or a compromised development environment) can still attempt to guess account passwords, API keys, or session tokens protected by the application layer.
Chi routes are often defined as a pipeline of plugs, and if rate limiting or account lockout is not explicitly applied before authentication checks, an attacker can iterate over credentials with low per-request cost. Even with mTLS ensuring that only certificate-holding clients reach the endpoint, the application must still treat every request as potentially malicious. Without per-user rate limits, account lockout, or CAPTCHA challenges after repeated failures, the mTLS boundary does not stop credential stuffing or online password guessing. Furthermore, mTLS does not prevent abuse at the HTTP layer: an attacker can cycle through usernames or use distributed client certificates to bypass IP-based throttling, making detection harder.
Consider a Chi pipeline that authenticates via client certificate and then delegates to a session-based login. If the login route does not enforce strict rate limits independent of mTLS success, an attacker can attempt thousands of password guesses per second from a whitelisted certificate. The mTLS handshake will succeed, but the application’s authorization logic remains the final gate; weaknesses there are highlighted by scans such as those performed by middleBrick, which tests authentication, BOLA/IDOR, and rate limiting in parallel. A scan can surface scenarios where mTLS protects transport but the API remains vulnerable to online brute force because controls are implemented at the wrong layer.
Real-world attack patterns mirror this: an attacker uses valid mTLS credentials to probe enumeration-friendly endpoints, looking for subtle timing differences or error messages that reveal whether a username exists. OWASP API Top 10 categories such as Broken Object Level Authorization and Security Misconfiguration intersect with brute force risks when mTLS is not coupled with application-level throttling and monitoring. middleBrick’s checks for Authentication, Rate Limiting, and BOLA/IDOR can identify whether your Chi service exposes endpoints where brute force can succeed despite mTLS, and it reports findings with severity and remediation guidance to help you prioritize fixes.
Mutual Tls-Specific Remediation in Chi — concrete code fixes
To harden a Chi application using mTLS against brute force, layer transport security with application-level controls. Ensure that rate limiting and account lockout are applied before or independent of mTLS authentication, and avoid leaking account existence through timing or messages. Below are focused code examples that demonstrate how to implement mTLS in Chi while adding brute force protections.
Enabling mTLS in a Chi router
Use Plug.SSL to require client certificates and validate them against a trusted CA. This ensures only clients with a valid certificate can proceed, but remember this alone does not stop credential guessing.
defmodule MyApp.Router do
use Plug.Router
plug Plug.SSL,
certs: [File.read!("path/to/ca_cert.pem")],
verify: :verify_peer,
depth: 1,
fail_if_no_peer_cert: true
plug Plug.Parsers, parsers: [:json]
plug Plug.Router
Rate limiting per identity before authentication checks
Apply rate limiting based on a stable identifier extracted from the certificate (such as the Common Name or a custom extension). This prevents an attacker from cycling through usernames or tokens from the same client cert.
defmodule MyApp.RateLimit do
use GenServer
def start_link(opts) do
GenServer.start_link(__MODULE__, :ok, opts)
end
def init(:ok) do
{:ok, %{}}
end
def allow?(key, limit, interval_ms) do
GenServer.call(__MODULE__, {:allow, key, limit, interval_ms})
end
def handle_call({:allow, key, limit, interval_ms}, _from, state) do
now = System.monotonic_time(:millisecond)
thresholds = Map.get(state, key, [])
recent = Enum.filter(thresholds, &(&1 > now - interval_ms))
if length(recent) >= limit do
{:reply, false, state}
else
updated = [now | recent]
{:reply, true, Map.put(state, key, updated)}
end
end
end
# In your pipeline plug
plug MyApp.RateLimit, key: &cert_common_name/1, limit: 10, interval_ms: 60_000
Avoid username enumeration in login responses
Ensure that login responses are uniform regardless of whether the username exists. Combine this with mTLS so that authentication proceeds only after both certificate validation and credentials are verified.
defmodule MyApp.Auth do
def login(conn, %{"username" => username, "password" => password}) do
user = Accounts.get_user_by_username(username)
case user do
nil ->
# Still verify a dummy hash to keep timing consistent
Argon2.hash_pwd_salt("dummy")
send_unauthorized(conn)
_ ->
if Argon2.verify_pass(password, user.password_hash) do
# Issue session/token
succeed(conn, user)
else
send_unauthorized(conn)
end
end
end
defp send_unauthorized(conn) do
conn
|> Plug.Conn.put_status(401)
|> json(%{error: "Unauthorized"})
|> halt()
end
end
Combine mTLS identity with per-user throttling
Extract a principal from the client certificate and enforce per-principal throttling at the application layer, complementing mTLS transport guarantees. This aligns with checks run by middleBrick for BOLA/IDOR and Rate Limiting, ensuring coverage across both transport and logic layers.
# Assuming the certificate common name is the subject
conn
|> fetch_client_cert_common_name()
|> case do
nil -> halt_unauthorized(conn)
principal ->
if MyApp.RateLimit.allow?(principal, 5, 60_000) do
proceed_to_authentication(conn)
else
halt_too_many_requests(conn)
end
end
These examples show how to integrate mTLS with explicit rate limiting and uniform error handling in Chi. By coupling transport identity checks with application-level controls, you reduce the risk of brute force attacks while maintaining a clear separation of concerns. middleBrick can validate that your implementation covers Authentication, Rate Limiting, and BOLA/IDOR, providing prioritized findings and remediation guidance to keep your API secure.