Insecure Deserialization in Chi with Mutual Tls
Insecure Deserialization in Chi with Mutual Tls
Insecure deserialization occurs when an application processes untrusted serialized objects without sufficient validation, enabling attackers to manipulate object graphs and execute unintended logic or code. In the Chi routing ecosystem for Clojure, this risk can intersect with Mutual TLS (mTLS) in nuanced ways. mTLS ensures both client and server present valid certificates, strengthening identity assurance for inbound requests. However, once a request is accepted, the application may still deserialize JSON, EDN, or other formats that were not designed with integrity guarantees for object graphs.
Consider a Chi endpoint that receives an mTLS-authenticated request containing a serialized user profile. If the handler uses an unsafe deserializer—such as reading EDN via clojure.edn/read-string on user-controlled input—an attacker who can present a valid client certificate could embed malicious payloads. These payloads might trigger remote code execution when the deserialized data is later evaluated, or they might exploit gadget chains present in the runtime to perform BOLA by tampering with identifiers embedded within the object graph. Even with mTLS providing transport-level identity, deserialization vulnerabilities persist because the trust boundary shifts from transport to data interpretation.
Common patterns that increase risk include using read-string or load-string on request bodies, or relying on libraries that internally deserialize without schema validation. Attack techniques relevant here include gadget chains that leverage Java serialization or Clojure’s reflective capabilities to invoke sensitive methods. Since mTLS suppresses some anonymous access risks, attackers may focus on abusing authorized sessions to test deserialization paths, making instrumentation and strict input validation essential.
Real-world parallels can be seen in Java deserialization CVEs such as CVE-2015-4852, where gadget chains in common libraries enabled remote code execution. In the Chi context, similar risks arise when the runtime processes crafted serialized input. OWASP API Top 10 categories such as Broken Object Level Authorization (BOLA) often intersect with deserialization issues, as malicious object graphs can elevate or impersonate identities when object references are not strictly constrained.
To detect these issues, scanning tools evaluate whether endpoints accept serialized input and apply unsafe deserialization functions. They also check whether mTLS is enforced at the endpoint and whether additional data validation is applied after authentication. Without runtime schema checks and strict type constraints, mTLS alone cannot prevent deserialization-based attacks.
Mutual Tls-Specific Remediation in Chi
Securing Chi endpoints against insecure deserialization while leveraging Mutual TLS involves a layered approach: enforce mTLS correctly, avoid unsafe deserialization, and validate data with schemas. Below are concrete code examples demonstrating these practices.
Mutual TLS setup in Chi
Configure the embedded Jetty server to require client certificates and validate them against a trusted CA. This ensures only authenticated clients can reach your handlers.
(ns myapp.tls
(:require [cheshire.core :as json]
[clj-http.client :as client]))
(defn create-server-context
"Create an SSL context that requests and validates client certificates."
[key-path cert-path ca-path]
(let [key-store (doto (java.security.KeyStore/getInstance "PKCS12")
(.load (java.io.FileInputStream. key-path) "changeit"))
trust-store (java.security.KeyStore/getInstance "JKS")
_ (.load trust-store (java.io.FileInputStream. ca-path))
ssl-context (javax.net.ssl.SSLContexts/custom)
kmf (doto (javax.net.ssl.KeyManagerFactory/getInstance "SunX509")
(.init key-store "changeit"))
tmf (doto (javax.net.ssl.TrustManagerFactory/getInstance "PKIX")
(.init trust-store))]
(.setKeyManager ssl-context kmf)
(.setTrustManager ssl-context tmf)
(.setNeedClientAuth ssl-context true)
(.build ssl-context)))
(defn wrap-mtls [handler ssl-context]
(fn [request]
(let [ssl-session (:ssl-session request)]
(if ssl-session
(handler request)
{:status 400 :body "Client certificate required"}))))
;; In your server start function:
;; (def server (jetty/run-jetty app {:port 8443 :ssl-port 8443 :ssl-context (create-server-context "resources/keystore.p12" "resources/cert.p12" "resources/cacerts")}))
Safe data handling after authentication
Once mTLS has validated the client, avoid unsafe deserialization. Use strict parsing with schema validation for JSON and EDN, and never evaluate raw strings.
(ns myapp.routes
(:require [cheshire.core :as json]
[malli.core :as m]
[malli.validate :as mv]
[ring.middleware.anti-forgery :refer [anti-forgery-middleware]]
[compojure.core :refer [POST defroutes]]))
(def user-schema
[:map
[:id :int]
[:name :string]
[:email [:re string? #".+@.+\..+"]]])
(defn validate-body [body schema]
(let [result (mv/validate schema body)]
(if (mv/qualified? result)
{:status 400 :body (str "Invalid input: " result)}
body)))
(defroutes api-routes
(POST "/profile" [request] ; request is already mTLS-authenticated
(let [body (some-> request :body slurp)
parsed (try (json/parse-string body true) (catch Exception _ nil))]
(if (and parsed (mv/validate user-schema parsed))
{:status 200 :body (str "Valid profile for " (:name parsed))}
{:status 400 :body "Invalid or malicious payload"}))))
;; Wrap with anti-forgery and other middleware as appropriate
;; (def secured-app (anti-forgery-middleware api-routes))
Operational practices
Ensure that deserialization logic is limited to trusted internal formats. If you must accept serialized data, enforce strict schemas and reject unknown fields. Combine this with continuous scanning that checks for insecure deserialization patterns and validates mTLS enforcement. These measures reduce the attack surface even when client identity is verified at the transport layer.