HIGH memory leakchimutual tls

Memory Leak in Chi with Mutual Tls

Memory Leak in Chi with Mutual Tls — how this specific combination creates or exposes the vulnerability

A memory leak in a Chi application using mutual TLS (mTLS) typically arises when request or connection resources are retained beyond their intended lifetime, and mTLS amplifies the impact because each authenticated connection carries additional cryptographic state. In Chi, a common pattern is to attach per-request context (e.g., certificates, principals, or custom metadata) via with-middleware or request-scoped maps. If these attachments are stored in long-lived structures or caches without proper cleanup, the associated memory—including TLS session state and certificate references—cannot be reclaimed, leading to a leak.

Mutual TLS introduces per-connection overhead, such as certificate verification results and secure session contexts. When Chi handlers or middleware retain references to request objects, response writers, or context values beyond the request lifecycle (for example, by appending to a global slice or caching responses without eviction), the associated mTLS structures remain referenced. This prevents garbage collection from freeing memory, and under sustained traffic the process grows in memory. The leak is often exposed through increased RSS memory and eventual latency or rejection due to resource exhaustion, which mTLS can exacerbate because each connection holds more state than plaintext HTTP.

Another scenario specific to mTLS is improper reuse or mishandling of the TLS configuration across handlers. For instance, if you derive per-request values from the client certificate (such as via ssl-client-certificate) and store them in a closure or package-level variable without synchronization, you may inadvertently keep references to large objects or buffers. Because mTLS requires validating each client certificate on every connection, the repeated creation of these objects without cleanup can manifest as a gradual memory increase, detectable via Chi’s request metrics and system-level memory profiling.

In practice, you can observe this pattern when endpoints that should be short-lived begin to show growing memory usage in monitoring, alongside increased CPU due to garbage collection. The combination of Chi’s composable middleware and mTLS’s heavier per-request cryptographic work makes it especially important to ensure that no references—especially those derived from TLS context—escape their request scope.

Mutual Tls-Specific Remediation in Chi — concrete code fixes

To fix memory leaks in Chi with mutual TLS, ensure that all request-scoped data, including anything derived from the TLS connection state, is confined to the request lifecycle and not captured by long-lived structures. Use Chi’s built-in request context and avoid attaching large or long-lived objects to the context or global caches. Below are concrete patterns and code examples.

1. Avoid capturing request or TLS-derived data in globals

Do not append request or TLS metadata to package-level variables. Instead, process and release within the handler scope.

(ns myapp.core
  (:require [cheshire.core :as json]
            [ring.adapter.jetty :as jetty]
            [ring.middleware.ssl :as ssl]
            [compojure.core :refer [GET defroutes]]))

(defonce connections-seen (atom #{})) ;; Risk: can retain TLS session metadata

;; Avoid this pattern:
(defn insecure-handler [request]
  (let [client-cert (:ssl-client-certificate request) ;; mTLS field
        _ (swap! connections-seen conj client-cert)]   ;; retains reference
    {:status 200 :body "ok"}))

;; Prefer this scoped processing:
(defn safe-handler [request]
  (let [client-cert (:ssl-client-certificate request)]
    ;; Use the value immediately without storing it globally
    {:status 200 :body (str "cert fingerprint: " (some-fingerprint client-cert))}))

2. Ensure middleware does not retain request context

When using middleware that inspects mTLS fields, avoid capturing the request or its context beyond the call. Do not accumulate per-request data in outer structures.

(ns myapp.middleware
  (:require [ring.util.request :as req]))

(defn wrap-client-audit [handler]
  (fn [request]
    (let [cert (get-in request [:ssl-client-certificate])]
      ;; Do not store cert or request in an external cache:
      (handler request)))) ;; request and cert are scoped to this invocation and released after

3. Properly configure and reuse TLS resources without over-retention

When setting up Jetty with mTLS in Chi, configure the SSL context at the adapter level and avoid creating per-request state that references the connection or certificate beyond what is necessary.

(ns myapp.server
  (:require [cheshire.core :as json]
            [ring.adapter.jetty :as jetty]
            [ring.middleware.ssl :as ssl]
            [compojure.core :refer [GET defroutes routes]]))

(defn app [request]
  {:status 200 :body "hello"})

(def wrapped-app
  (-> app
      (ssl/wrap-ssl-redirect {:port 8443
                              :truststore {:store-type "JKS"
                                           :store-path "keystore.jks"
                                           :password "changeit"}
                              :cert-store {:store-type "JKS"
                                           :store-path "keystore.jks"
                                           :password "changeit"}
                              :need-client-auth true})))

(defn -main []
  (jetty/run-jetty wrapped-app {:port 8443
                                :max-idle-time 60000
                                :ssl? true}))

4. Validate and clean up resources in request finalization

If you cache or process data derived from mTLS, ensure cleanup via request finalization or response callbacks to release references promptly.

(ns myapp.cleanup
  (:require [compojure.core :refer [GET defroutes]]))

(defn handler-with-cleanup [request]
  (let [client-cert (:ssl-client-certificate request)
        resp {:status 200 :body "done"}]
    (try
      resp
      (finally
        ;; Explicitly clear locally scoped references if needed
        ))))

5. Use Chi’s routing and context functions carefully

When using `assoc` or `update` on the request map inside Chi routes, prefer local transformations and avoid storing the mutated request in external state.

(ns myapp.routes
  (:require [compojure.core :refer [GET defroutes]]))

(defn process-mtls [request]
  (let [cert (get-in request [:ssl-client-certificate])]
    ;; Local processing, no external retention
    (if cert
      {:status 200 :body "verified"}
      {:status 400 :body "no client cert"})))

(defroutes app-routes
  (GET "/secure" request (process-mtls request)))

Frequently Asked Questions

How can I detect a memory leak in Chi when using mutual TLS?
Monitor process memory (RSS) and GC activity under load; use profiling tools to identify retained objects tied to TLS context, and audit middleware that captures request or certificate data in long-lived stores.
Does middleBrick detect memory leaks in Chi with mutual TLS?
middleBrick scans unauthenticated attack surfaces and reports security risk findings; it does not detect or report memory leaks. Use runtime profiling and observability tools for memory analysis.