Timing Attack in Chi with Mutual Tls
Timing Attack in Chi with Mutual Tls — how this specific combination creates or exposes the vulnerability
A timing attack in the context of Chi with Mutual TLS occurs when an attacker can infer private information—such as certificate validity, username existence, or token correctness—by measuring the time taken to establish a TLS handshake. Even with Mutual TLS, where both client and server present certificates, subtle differences in server-side processing can introduce measurable latency that leaks state.
Chi is a functional web framework for Clojure that emphasizes composability and explicit data flow. When developers build HTTPS endpoints in Chi, they often terminate TLS at the edge (e.g., a load balancer) or within the application using libraries like malli for schema validation and custom middleware for certificate handling. If certificate validation or token verification is performed in a non-constant-time manner—such as iterating over certificate fields or comparing digests with early-exit logic—an attacker observing round-trip times may deduce whether a particular certificate or claim is valid.
For example, suppose a Chi service validates client certificates by checking a fingerprint against a list. If the comparison stops at the first match, the time taken will vary depending on the position of the matching certificate. An attacker who can send many connections and measure response times may use statistical analysis to recover the valid fingerprint. Similarly, if the server performs additional work—such as fetching user-specific data or constructing detailed error messages—only when a certificate is accepted, the absence of that work for rejected certificates creates a distinguishable timing delta.
Mutual TLS does not eliminate these risks; it changes the threat model. The server must still verify the client certificate, and if that verification is not constant-time, the handshake duration can reveal whether the certificate was trusted, whether the subject matched an expected value, or whether authorization checks passed. In Chi, this often manifests in middleware that branches based on certificate metadata. Because TLS handshakes in Chi typically occur before requests reach business logic, timing leaks can persist across endpoints that share the same TLS configuration.
Real-world attack patterns aligned with this risk include adaptations of the Lucky Thirteen or similar timing-based oracle attacks, where network measurements are combined with knowledge of the TLS implementation to infer details. Although Chi does not prescribe a specific TLS library, the framework’s compositional nature means that developers must ensure any per-request logic tied to certificate validation executes in constant time to prevent information leakage through timing channels.
Mutual Tls-Specific Remediation in Chi — concrete code fixes
To mitigate timing attacks in Chi with Mutual TLS, ensure that all certificate validation and comparison steps execute in constant time, independent of secret values. Avoid branching logic that changes based on certificate contents, and standardize response behavior for accepted and rejected connections.
Below are concrete code examples demonstrating a constant-time validation approach in Chi.
Constant-time certificate fingerprint comparison
(ns myapp.tls
(:require [cheshire.core :as json]
[ring.util.response :as resp]))
(defn constant-time-equals
"Compare two byte arrays in constant time to avoid timing leaks."
[a b]
(when (= (count a) (count b))
(reduce (fn [acc [xa xb]]
(bitwise-or acc (bit-xor xa xb))) 0 (map vector a b))))
(defn validate-client-fingerprint
"Validate the client certificate fingerprint in constant time."
[expected-fingerprint request]
(let [client-cert (some-> request :ssl-client-cert :fingerprint :sha256)
match (constant-time-equals expected-fingerprint client-cert)]
(zero? match)))
(defn secure-handler
"A Chi handler that uses constant-time validation."
[{{:keys [ssl-client-cert]} :ssl :as request}]
(if (validate-client-fingerprint expected-fingerprint request)
(resp/response {:status :ok})
(resp/response {:status :unauthorized})))
Standardized TLS middleware in Chi routes
(ns myapp.routes
(:require [compojure.core :refer [defroutes GET]]
[ring.middleware.anti-forgery :refer [anti-forgery-field]]
[myapp.tls :refer [validate-client-fingerprint]]))
(defn wrap-tls-constants [handler]
(fn [request]
;; Ensure every request goes through constant-time checks
(if (validate-client-fingerprint expected-fingerprint request)
(handler request)
{:status 403
:headers {"Content-Type" "application/json"}
:body (json/generate-string {:error "forbidden"})}))
)
(defroutes app-routes
(GET "/api/secure" [] "secure response")
)
(def app
(wrap-tls-constants app-routes))
In these examples, constant-time-equals ensures that comparison duration does not depend on where the first mismatch occurs. The handler applies the same validation path for all requests, so timing behavior does not reveal whether a certificate was valid or which field failed. This approach aligns with best practices for cryptographic comparisons and helps prevent timing-based oracle attacks in a Chi application using Mutual TLS.