MEDIUM graphql introspectionactixmutual tls

Graphql Introspection in Actix with Mutual Tls

Graphql Introspection in Actix with Mutual Tls — how this specific combination creates or exposes the vulnerability

GraphQL introspection in an Actix web service allows clients to query the schema, types, and operations dynamically. When mutual TLS (mTLS) is used for transport-layer authentication, the assumption is often that only authorized clients can reach the endpoint. However, mTLS ensures client identity but does not restrict what an authenticated client can query. If introspection is enabled in production, any mTLS-authenticated client can retrieve the full schema, including queries, mutations, subscriptions, and input types. This can expose field names, relationships, and business logic that you might intend to keep private.

In an Actix-based GraphQL server (for example, using async-graphql or juniper with Actix-web), enabling introspection is common during development. When combined with mTLS, an authenticated but potentially untrusted client can probe for sensitive operations such as exportData or internalHealth. Attackers with valid client certificates can automate introspection queries to map the API surface, which can aid in crafting further attacks like BOLA (Broken Object Level Authorization) by identifying object IDs and nested relations. The risk is compounded if the schema includes overly permissive queries or if field-level authorization is implemented only at the resolver level without schema-level restrictions.

Consider a scenario where your Actix GraphQL endpoint is reachable at https://api.example.com/graphql and mTLS is enforced by the Actix-web middleware. An authenticated attacker can send an introspection query over the mutually authenticated TLS channel to learn the schema. For example, the following query is what an attacker would submit to enumerate operations and types:

{ __schema { queryType { name } mutationType { name } types { name kind possibleTypes { name } } } }

If the server responds with a full schema, the attacker gains knowledge of mutation names like deleteUser or rotateApiKey, which can then be targeted for IDOR or BOLA if authorization checks are weak. Since mTLS does not limit introspection by itself, the combination creates a pathway for information disclosure that should be explicitly controlled.

Mutual Tls-Specific Remediation in Actix — concrete code fixes

To secure an Actix GraphQL service using mutual TLS while mitigating introspection risks, you must enforce mTLS at the web layer and explicitly disable introspection in production. Below are concrete patterns and configuration examples.

1. Enforce mTLS in Actix-web

Use Rust TLS acceptor with client certificate verification. The following example configures an Actix-web server that requires client certificates and validates them against a trusted CA:

use actix_web::{web, App, HttpServer}; 
use actix_web_httpauth::extractors::AuthenticationError; 
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod, SslVerifyMode}; 

fn create_ssl_acceptor() -> std::io::Result<SslAcceptor> { 
    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls_server())?; 
    builder.set_private_key_file("keys/server.key", SslFiletype::PEM)?; 
    builder.set_certificate_chain_file("certs/server.crt")?; 
    builder.set_client_ca_file(&["certs/ca.crt"])?; 
    builder.set_verify(SslVerifyMode::PEER | SslVerifyMode::FAIL_IF_NO_PEER_CERT, 
        |_ssl, cert| cert.subject_name().find_entry_by_nid(nid::COMMONNAME).is_some()); 
    Ok(builder.build()) 
} 

#[actix_web::main] 
async fn main() -> std::io::Result<()> { 
    let ssl = create_ssl_acceptor().expect("SSL acceptor"); 
    HttpServer::new(|| { 
        App::new() 
            .wrap(actix_web_httpauth::HttpAuthentication::basic("auth", |req, credentials| { 
                // optional additional app-level auth 
                future::ok(()) 
            })) 
            .service(web::resource("/graphql").to(graphql_handler)) 
    }) 
    .bind_openssl("127.0.0.1:8443", ssl)? 
    .run() 
    .await 
} 

This ensures that only clients presenting a certificate signed by the trusted CA can establish a TLS connection. However, mTLS alone does not prevent introspection, so you must also disable it explicitly.

2. Disable introspection in production

If you use async-graphql, you can disable introspection by configuring the schema:

use async_graphql::*; 
use async_graphql_actix_web::*; 

struct Query; 
#[Object] 
impl Query { 
    async fn hello(&self) -> &str { "world" } 
} 

#[actix_web::main] 
async fn main() { 
    let schema = Schema::build(Query, EmptyMutation, EmptySubscription) 
        .disable_introspection() // disables introspection queries 
        .finish(); 

    HttpServer::new(move || { 
        App::new() 
            .app_data(web::Data::new(schema.clone())) 
            .service(graphql::post("/graphql")) 
    }) 
    .bind("127.0.0.1:8080")? 
    .run() 
    .await 
    .unwrap(); 
}

For juniper-based services, disable introspection via GraphQLWalk::introspection_method or by not including the introspection fields in your root object. In production, ensure introspection is disabled while keeping mTLS for client authentication. This prevents authenticated clients from enumerating operations and types, reducing the attack surface exposed through the GraphQL layer.

Related CWEs: dataExposure

CWE IDNameSeverity
CWE-200Exposure of Sensitive Information HIGH
CWE-209Error Information Disclosure MEDIUM
CWE-213Exposure of Sensitive Information Due to Incompatible Policies HIGH
CWE-215Insertion of Sensitive Information Into Debugging Code MEDIUM
CWE-312Cleartext Storage of Sensitive Information HIGH
CWE-359Exposure of Private Personal Information (PII) HIGH
CWE-522Insufficiently Protected Credentials CRITICAL
CWE-532Insertion of Sensitive Information into Log File MEDIUM
CWE-538Insertion of Sensitive Information into Externally-Accessible File HIGH
CWE-540Inclusion of Sensitive Information in Source Code HIGH

Frequently Asked Questions

Does mutual TLS prevent GraphQL introspection?
No. Mutual TLS authenticates clients but does not restrict what an authenticated client can query. You must explicitly disable introspection to prevent schema discovery.
How can I test if introspection is disabled in my Actix GraphQL endpoint?
Send a POST request with the introspection query `{ __schema { queryType { name } } }` over your mTLS-enabled endpoint. A properly secured service should respond with an error indicating introspection is disabled rather than returning the schema.