Graphql Introspection in Chi with Mutual Tls
Graphql Introspection in Chi with Mutual Tls — how this specific combination creates or exposes the vulnerability
GraphQL introspection in Chi can expose schema details even when the endpoint is protected by Mutual TLS (mTLS). Introspection queries (e.g., __schema { queryType { name } }) return full type definitions, which can reveal internal models, queries, and business logic. When mTLS is used, the server validates client certificates, but if the route handling GraphQL requests does not enforce strict authorization, an authenticated client may still issue introspection queries. This creates a risk of information disclosure because mTLS ensures identity but does not limit what authenticated clients can query.
In Chi, this typically occurs when a GraphQL handler is mounted under a route that lacks per-operation or per-field authorization checks. Because introspection is often enabled by default in development and sometimes left accessible in production for debugging, an attacker with a valid client certificate (obtained through compromised credentials or provisioning) can retrieve the schema and plan further attacks, such as BOLA/IDOR or property authorization flaws. The combination therefore does not introduce a new protocol weakness, but it can amplify the impact of missing operation-level controls.
Real-world findings from scans often map this to OWASP API Top 10:2023 A1 — Broken Object Level Authorization, where valid mTLS-authenticated sessions can still run introspection or queries against unauthorized resources. For example, a query like { user(id: "123") { email } } may be allowed simply because the request presents a valid certificate, even though the user context should restrict fields. middleBrick detects such scenarios under its Property Authorization and BOLA/IDOR checks, highlighting that mTLS alone is insufficient to prevent information leakage via introspection.
Mutual Tls-Specific Remediation in Chi — concrete code fixes
To secure GraphQL endpoints in Chi when using Mutual TLS, you must combine mTLS with explicit authorization for introspection and query operations. This means checking scopes or claims from the client certificate before allowing introspection, and applying field-level or operation-level permissions based on authenticated identity.
Example: Conditional Introspection in Chi
Allow introspection only for specific authenticated roles (e.g., clients with a particular certificate extended key usage or SAN). Use Chi routes and middleware to inspect the TLS client certificate and decide whether introspection is permitted.
// src/graphql_handler.clj
(ns myapp.graphql-handler
(:require [cheshire.core :as json]
[clojure.string :as str]
[io.pedestal.http :as http]
[io.pedestal.http.route :as route]
[com.walmartlabs.lacinia.execute :as exec]
[com.walmartlabs.lacinia.schema :as schema]))
(defn client-certificate-allowed? [request]
(let [client-cn (get-in request [:ssl-client-cert :common-name])]
(contains? #{"trusted-admin" "schema-reader"} client-cn)))
(defn graphql-handler [request]
(if-let [query (:query (http/body request))]
(if (and (= "__schema" (some-> query str/lower-case))
(not (client-certificate-allowed? request)))
{:status 403
:body (json/generate-string {:error "introspection not allowed"})}
;; proceed with schema execution
{:status 200
:body (json/generate-string (exec/execute schema/query-schema query))})
{:status 400
:body (json/generate-string {:error "missing query"})}))
(def service
{::http/routes [["/graphql" {:post graphql-handler}]]
::http/chain-validator (fn [request] (when-not (get-in request [:ssl-client-cert])
(throw (ex-info "mTLS required" {:status 401}))))
::http/type :jetty
::http/port 8443})
Example: Role-Based Field Authorization
Even with mTLS, ensure that fields like email or internal IDs are only accessible to authorized roles. This example demonstrates wrapping the GraphQL execution to enforce claims-based checks.
// src/myapp/authorization.clj
(ns myapp.authorization
(:require [clojure.set :as set]))
(defn authorized-fields [request permitted-fields]
(let [client-roles (get-in request [:ssl-client-cert ::roles])]
(if (some #(contains? client-roles %) #{"admin" "support"})
permitted-fields
(set/difference permitted-fields #{:User_email :Account_ssn})}))
(defn build-schema-with-auth []
(schema/merge-schemas
{:schema-doc "type Query { user(id: ID!): User } type User { id: ID! email: String }"}
{:resolvers {:Query
{:user (fn [_ _ ctx value args]
(let [permitted (authorized-fields ctx #{:User_id :User_name})]
(if (contains? permitted :User_email)
{:id value :email "[email protected]"}
{:id value}))))}}}})
With these patterns, mTLS in Chi provides strong client authentication, while explicit checks prevent introspection abuse and over-fetching. middleBrick’s LLM/AI Security and Property Authorization checks can validate that introspection is appropriately gated and that roles align with intended access boundaries.
Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |