Jwt Misconfiguration in Chi with Mutual Tls
Jwt Misconfiguration in Chi with Mutual Tls — how this specific combination creates or exposes the vulnerability
Chi is a small, functional routing library for Clojure services. When you use Mutual TLS (mTLS) with Chi, the server is configured to request and validate client certificates. This transport-layer setup can create a false sense of security and lead to JWT misconfiguration if developers assume mTLS alone replaces or fulfills authentication and authorization checks. For example, a Chi route may skip JWT validation because the request already arrived over a mutually authenticated TLS channel, leading to missing middleware that verifies tokens, audiences, issuers, or scopes.
A concrete misconfiguration pattern is omitting JWT verification middleware when mTLS is enforced. Consider this incomplete Chi handler:
(defroutes app-routes
(GET "/profile" req
;; BUG: No JWT validation present; relies only on mTLS client identity
(profile-response req)))
In this setup, mTLS ensures the client possesses a valid certificate trusted by the server, but it does not validate the JWT’s contents, expiry, or intended audience. An attacker who compromises a valid certificate can send crafted requests with arbitrary or missing JWTs and still reach the endpoint. Additionally, if the JWT is validated but the Chi route incorrectly extracts claims (e.g., using the certificate subject instead of the JWT payload), authorization bypass can occur because roles or scopes in the certificate do not match those encoded in the token.
Another specific risk arises from inconsistent clock or key management. mTLS certificates often have long lifetimes, while JWTs are typically short-lived with strict time windows. If the application does not enforce JWT exp/nbf checks or does not validate the issuer (iss) and audience (aud) claims in the context of mTLS, tokens issued for other services or environments may be accepted. This becomes critical in shared or multi-tenant deployments where mTLS certificates are issued broadly but JWTs should be tightly scoped.
Finally, logging and error handling in Chi can inadvertently expose JWT-related information when mTLS is used. For instance, returning detailed errors when JWT validation fails can reveal whether mTLS was presented, aiding reconnaissance. Proper remediation requires explicit JWT validation middleware that runs regardless of mTLS status, strict claim checks, and disciplined separation of transport identity and token-based identity.
Mutual Tls-Specific Remediation in Chi — concrete code fixes
To securely combine mTLS and JWT validation in Chi, enforce JWT checks as a separate, explicit middleware layer. Do not rely on the presence of a client certificate to imply authorization. The following example shows a corrected Chi application with mTLS server configuration and mandatory JWT validation.
(require '[cheshire.core :as json]
'[buddy.sign.jwt :as jwt]
'[ring.util.response :refer [response unauthorized]]
'[compojure.core :refer [defroutes GET]]
'[compojure.route :as route])
(defn validate-jwt [req]
(let [token (some-> req :headers (get "authorization") (re-find #"Bearer\s+(.+)") second)
public-key (some-fn ... )] ;; load your public key or JWKS
(try
(jwt/unsign token public-key {:alg "RS256"})
(catch Exception _
(unauthorized "Invalid or missing JWT")))))
(def app
(-> (validate-jwt)
(wrap-key-params)
(-> (fn [handler]
(fn [req]
(if (= (:ssl-client-verify req) "SUCCESS")
(handler req)
{:status 400 :body "mTLS client certificate required"}))))
(wrap-routes app-routes)))
(defroutes app-routes
(GET "/profile" req
(response {:user (get-in req [:jwt/claims :sub])})))
In this example, validate-jwt is a dedicated middleware that extracts the Bearer token from the Authorization header and validates it using a public key. The mTLS check is performed by a separate wrapper that ensures :ssl-client-verify is SUCCESS before allowing the request to proceed. This guarantees both layers are enforced independently.
When deploying with an mTLS-capable reverse proxy or load balancer in front of Chi, configure the proxy to terminate TLS and forward the client certificate information via headers (e.g., SSL-Client-Verify, SSL-Client-DN). Chi can then inspect these headers as shown above while still validating JWTs at the application level. Avoid disabling JWT checks when mTLS headers are present; instead, treat mTLS as transport authentication and JWT as resource-level authorization.
For key management, prefer a JWKS endpoint and rotate public keys programmatically rather than hardcoding them. In CI/CD, include integration tests that send requests with valid mTLS but invalid JWTs and vice versa to confirm both checks are active. Tools like middleBrick can help by scanning your Chi API endpoints to detect missing JWT validation or inconsistent claim handling, even when mTLS is configured.
Related CWEs: authentication
| CWE ID | Name | Severity |
|---|---|---|
| CWE-287 | Improper Authentication | CRITICAL |
| CWE-306 | Missing Authentication for Critical Function | CRITICAL |
| CWE-307 | Brute Force | HIGH |
| CWE-308 | Single-Factor Authentication | MEDIUM |
| CWE-309 | Use of Password System for Primary Authentication | MEDIUM |
| CWE-347 | Improper Verification of Cryptographic Signature | HIGH |
| CWE-384 | Session Fixation | HIGH |
| CWE-521 | Weak Password Requirements | MEDIUM |
| CWE-613 | Insufficient Session Expiration | MEDIUM |
| CWE-640 | Weak Password Recovery | HIGH |