HIGH graphql introspectionactixcockroachdb

Graphql Introspection in Actix with Cockroachdb

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

GraphQL introspection in an Actix service that uses CockroachDB as the backend can expose your schema, data structure, and operational details to unauthenticated attackers. Introspection is a first-class GraphQL feature that returns full type definitions, queries, and relationships. When enabled on a public or improperly restricted endpoint, it allows an attacker to discover tables, columns, and relationships that map closely to your CockroachDB schema. This mapping can reveal sensitive design choices, such as how user records, API keys, or personally identifiable information are stored, which can inform further attacks like injection or IDOR.

In practice, a typical Actix GraphQL endpoint backed by CockroachDB may expose introspection by default during development and inadvertently remain enabled in production. An attacker can send an introspection query to enumerate types such as User, ApiKey, or AuditLog, revealing column names like email, password_hash, and created_at. Because CockroachDB is a distributed SQL database, schema details such as table names and indexes are part of the metadata returned by introspection. This becomes especially risky if the GraphQL schema closely mirrors database tables, effectively providing an attacker a blueprint for BOLA/IDOR or injection attempts.

When combined with other checks that middleBrick runs in parallel, introspection findings are prioritized alongside input validation and authentication tests. For example, if introspection reveals a password_reset_tokens table, an attacker can use that knowledge to test IDOR by manipulating user IDs in related queries. middleBrick’s LLM/AI Security checks further highlight risk when introspection exposes models or endpoints that could be targeted by prompt injection or data exfiltration attempts. The scanner correlates introspection exposure with rate limiting and data exposure checks to emphasize the potential impact of leaking schema details from CockroachDB through GraphQL.

To mitigate this specific vector while retaining introspection for development, you should disable introspection on public endpoints and enforce authentication for schema queries. In production, introspection should be limited to trusted internal tools. Using middleware in Actix to conditionally enable introspection based on request origin or authentication status reduces the attack surface. Additionally, schema stitching or partial schema definitions can be used to expose only necessary types, minimizing the information an attacker can gather about your CockroachDB-backed GraphQL API.

Cockroachdb-Specific Remediation in Actix — concrete code fixes

Remediation centers on disabling or protecting introspection at the Actix GraphQL layer while ensuring your CockroachDB queries remain functional for authorized use. Below are concrete, realistic examples showing how to configure introspection and secure schema access.

1. Disable introspection in production builds

Configure your GraphQL service in Actix to disable introspection when a feature flag or environment variable indicates production. This prevents runtime exposure while keeping development workflows functional.

use actix_web::{web, App, HttpServer, HttpResponse};
use async_graphql::{Schema, EmptyMutation, EmptySubscription, Object, Context};
use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};
use std::env;

struct QueryRoot;

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

async fn build_schema() -> Schema {
    Schema::build(QueryRoot, EmptyMutation, EmptySubscription)
        .enable_introspection(env::var("ENABLE_INTROSPECTION").unwrap_or("false".into()).parse().unwrap_or(false))
        .finish()
}

async fn graphql_handler(schema: web::Data<Schema<QueryRoot, EmptyMutation, EmptySubscription>>, req: GraphQLRequest) -> GraphQLResponse {
    schema.execute(req.into_inner()).await.into()
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let schema = web::Data::new(build_schema().await);
    HttpServer::new(move || {
        App::new()
            .app_data(schema.clone())
            .route("/graphql", web::post().to(graphql_handler))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

2. Restrict introspection via middleware based on roles or IPs

Use Actix middleware to allow introspection only for requests with specific headers or from trusted networks. This is useful when you want to keep introspection available for internal tooling while blocking external access to CockroachDB schema details.

use actix_web::{dev::ServiceRequest, Error, middleware::Next};
use actix_web::http::header::HeaderValue;

struct IntrospectionGuard;

impl actix_web::dev::Transform for IntrospectionGuard
where
    S: actix_web::dev::Service<actix_web::dev::ServiceRequest, Response = actix_web::dev::ServiceResponse, Error = Error>,
    S::Future: 'static,
{
    type Response = actix_web::dev::ServiceResponse;
    type Error = Error;
    type Transform = IntrospectionGuardMiddleware<S>;
    type InitError = ();
    type Future = std::future::Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        std::future::ready(Ok(IntrospectionGuardMiddleware { service }))
    }
}

struct IntrospectionGuardMiddleware<S> {
    service: S,
}

impl<S> actix_web::dev::Service<actix_web::dev::ServiceRequest> for IntrospectionGuardMiddleware<S>
where
    S: actix_web::dev::Service<actix_web::dev::ServiceRequest, Response = actix_web::dev::ServiceResponse, Error = Error>,
    S::Future: 'static,
{
    type Response = actix_web::dev::ServiceResponse;
    type Error = Error;
    type Future = std::pin::Pin<Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&self, cx: &mut std::task::Context<'_>) -> std::task::Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&self, req: actix_web::dev::ServiceRequest) -> Self::Future {
        let allow = req.headers().get("X-Introspection-Token")
            .and_then(|v| v.to_str().ok())
            .map(|v| v == "trusted-token");
        if req.path().contains("__schema") && !allow.unwrap_or(false) {
            let response = actix_web::error::ErrorForbidden("Introspection not allowed");
            return Box::pin(async { Err(response) });
        }
        let fut = self.service.call(req);
        Box::pin(async move { fut.await })
    }
}

3. Use CockroachDB role-based access with parameterized queries

Even when introspection is disabled, ensure your Actix services connect to CockroachDB with least-privilege roles and use parameterized queries to prevent injection. Below is a minimal example using the postgres crate with TLS and role-specific credentials loaded from environment variables.

use postgres::{Client, Config, NoTls};
use std::env;

fn get_db_client() -> Client {
    let host = env::var("DB_HOST").unwrap_or("localhost".into());
    let port = env::var("DB_PORT").unwrap_or("26257".into());
    let user = env::var("DB_USER").unwrap_or("app_reader".into());
    let password = env::var("DB_PASSWORD").unwrap_or_default();
    let dbname = env::var("DB_NAME").unwrap_or("app_db".into());

    let mut cfg = Config::new();
    cfg.host(&host)
       .port(port.parse().unwrap_or(26257))
       .user(&user)
       .password(password.as_bytes())
       .dbname(&dbname);
    // In production, use TLS via rustls or native-tls depending on your cluster setup
    cfg.connect(NoTls).expect("Failed to connect to CockroachDB")
}

async fn get_user_email(user_id: i32) -> Option<String> {
    let client = get_db_client();
    // Use parameterized query to avoid injection
    client.query_opt("SELECT email FROM users WHERE id = $1", &[&user_id])
        .ok()?
        .map(|row| row.get(0))
}

Combine these practices with middleBrick scans to validate that introspection is not publicly exposed and that your authentication and input validation controls are effective against known attack patterns such as injection and IDOR.

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

Should I disable GraphQL introspection entirely?
Disable introspection on public-facing endpoints in production to prevent schema exposure. You can keep it enabled for internal tooling or development, but restrict it via authentication or IP allowlists.
Does disabling introspection protect against all GraphQL-based attacks?
No. Introspection disclosure is one vector. You should also enforce strong input validation, rate limiting, authentication, and authorization checks to mitigate IDOR, injection, and privilege escalation.