HIGH llm data leakageactixmutual tls

Llm Data Leakage in Actix with Mutual Tls

Llm Data Leakage in Actix with Mutual Tls

LLM data leakage in an Actix web service occurs when prompts, model outputs, or sensitive runtime data are unintentionally exposed through endpoints, logs, or error messages. When mutual TLS (mTLS) is used for client authentication in Actix, the assumption is that only authorized clients can initiate connections. However, mTLS secures the transport and client identity; it does not automatically protect data that the application itself processes or returns. If an Actix handler passes user-controlled or attacker-influenced input into an LLM endpoint and then returns the LLM response to the client, any sensitive information in the prompt or exposed in the model output can leak to unauthorized parties. This risk persists even with mTLS because the vulnerability lies in application logic, not in transport authentication.

For example, consider an Actix route that forwards a user-supplied query to an LLM and streams the response back. If the query contains private data, and the response includes credentials, internal paths, or proprietary text, an authenticated client (with a valid certificate) can exfiltrate that data. The LLM data leakage checks in middleBrick specifically test for such scenarios by probing endpoints that accept input and produce LLM-generated output, looking for PII, API keys, and executable code in responses. Even with mTLS ensuring only known clients connect, an authenticated client can still trigger these leaks if the server does not sanitize inputs or restrict outputs.

In an mTLS-enabled Actix deployment, server-side code typically loads certificate authorities and configures identity verification. A developer might assume this prevents unauthorized access to the LLM endpoint, but if the handler does not enforce additional authorization or output validation, an authenticated client can still cause leakage. Attackers may use techniques like prompt injection to coerce the LLM into revealing training data or internal instructions, and mTLS will not stop this because the request is from a trusted client. middleBrick’s LLM/AI Security checks include active prompt injection probes (system prompt extraction, instruction override, DAN jailbreak, data exfiltration, cost exploitation) and output scanning for PII and secrets, which can surface these classes of issues in Actix services.

Using OpenAPI/Swagger spec analysis, middleBrick cross-references definitions with runtime findings to highlight endpoints where LLM interactions occur. For Actix, ensure that handlers validate and sanitize all inputs, apply strict schema checks on LLM prompts, and avoid returning raw model outputs that may contain sensitive content. Even with mTLS in place, implement per-request authorization, rate limiting, and output filtering to reduce the chance of data leakage. Continuous scanning with the Pro plan provides ongoing monitoring for such issues, and the CLI allows you to integrate checks into your development workflow.

Mutual Tls-Specific Remediation in Actix

To remediate LLM data leakage risks in Actix with mutual TLS, focus on application-level controls rather than relying on mTLS alone. Ensure that every handler that interacts with an LLM validates and sanitizes inputs, applies output filtering, and enforces authorization checks. Below are concrete code examples showing how to configure mTLS in Actix and structure handlers to minimize leakage.

First, configure Actix to require client certificates using the actix-web and openssl crates. The server must load a certificate authority (CA) to verify client certificates, and each request can then access the client identity for authorization decisions.

use actix_web::{web, App, HttpServer, Responder, HttpRequest};
use openssl::ssl::{SslAcceptor, SslFiletype, SslMethod};

fn create_ssl_acceptor() -> SslAcceptor {
    let mut builder = SslAcceptor::mozilla_intermediate(SslMethod::tls()).unwrap();
    builder.set_private_key_file("keys/server.key", SslFiletype::PEM).unwrap();
    builder.set_certificate_chain_file("certs/server.crt").unwrap();
    builder.set_client_ca_file("certs/ca.crt").unwrap();
    builder.set_verify(openssl::ssl::SslVerifyMode::PEER | openssl::ssl::SslVerifyMode::FAIL_IF_NO_PEER_CERT, 
        |_, _| true);
    builder.build()
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let ssl_acceptor = create_ssl_acceptor();
    HttpServer::new(|| {
        App::new()
            .wrap(actix_web_openssl::Openssl::new(ssl_acceptor.clone()))
            .route("/llm/query", web::post(). to(llm_query_handler))
    })
    .bind_openssl("127.0.0.1:8443", ssl_acceptor)?
    .run()
    .await
}

In the handler, extract the client certificate subject for authorization and validate all inputs before sending them to the LLM. Do not trust the client-supplied data even if the TLS client is verified.

use actix_web::{post, web, HttpResponse};
use serde::{Deserialize, Serialize};

#[derive(Deserialize)]
struct QueryRequest {
    prompt: String,
}

#[derive(Serialize)]
struct QueryResponse {
    answer: String,
}

#[post("/llm/query")]
async fn llm_query_handler(
    req: web::Json,
    client_cert: web::ReqData, // extracted from request extensions by middleware
) -> HttpResponse {
    // Authorize based on client_cert, e.g., check against allowed principals
    let principal = client_cert.into_inner();
    if !is_authorized(&principal) {
        return HttpResponse::Forbidden().body("Unauthorized");
    }

    // Sanitize and validate input before LLM call
    let sanitized_prompt = sanitize_input(&req.prompt);
    let llm_response = call_llm(&sanitized_prompt).await;

    // Filter output to remove potential PII or secrets before returning
    let safe_response = filter_output(&llm_response);
    HttpResponse::Ok().json(QueryResponse { answer: safe_response })
}

fn sanitize_input(prompt: &str) -> String {
    // Implement input validation/sanitization appropriate to your use case
    prompt.trim().to_string()
}

async fn call_llm(prompt: &str) -> String {
    // Perform the LLM call; ensure no sensitive data is logged
    "LLM answer".to_string()
}

fn filter_output(output: &str) -> String {
    // Remove or mask PII, API keys, code snippets before returning
    output.to_string()
}

fn is_authorized(principal: &str) -> bool {
    // Check principal against an allowlist or policy
    true
}

These examples show how to enforce mTLS at the transport level while applying input validation, output filtering, and per-request authorization in the application. This reduces the likelihood of LLM data leakage even when authenticated clients interact with endpoints that use LLMs.

Related CWEs: llmSecurity

CWE IDNameSeverity
CWE-754Improper Check for Unusual or Exceptional Conditions MEDIUM

Frequently Asked Questions

Does mTLS alone prevent LLM data leakage in Actix?
No. Mutual TLS authenticates clients and secures transport, but it does not protect against application-level data leakage. If handlers pass sensitive data to an LLM and return the output without validation, authenticated clients can still cause leakage. You must implement input sanitization, output filtering, and per-request authorization in addition to mTLS.
How can I test my Actix endpoints for LLM data leakage?
Use tools that perform active probing and output scanning for PII, API keys, and code. MiddleBrick’s LLM/AI Security checks include prompt injection tests and output scanning designed to surface leakage in authenticated flows. You can run scans via the CLI with the middlebrick scan command or integrate checks into CI/CD using the GitHub Action.