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 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 |