Insecure Deserialization in Axum
How Insecure Deserialization Manifests in Axum
In Axum, insecure deserialization commonly occurs when handlers accept user-controlled input and pass it directly to deserialization functions without validation. Axum's extractor system (like Json, Form, or custom extractors) can inadvertently expose this risk if misused. For example, using serde_json::from_str on raw request bodies extracted via axum::extract::Json bypasses Axum's built-in validation when the extractor is incorrectly implemented.
A real-world pattern involves accepting a serialized object in a header or query parameter, then deserializing it with libraries like bincode or postcard without type restrictions. Attackers can exploit this to achieve remote code execution (RCE) by crafting payloads that trigger dangerous deserialization gadgets, similar to CVE-2015-7501 in Java ecosystems but adapted to Rust's serialization frameworks.
In Axum, this often appears in middleware or route handlers where custom extraction logic is used. For instance, a handler expecting a JWT token might deserialize a user-supplied string using jsonwebtoken::decode with an unsafe key validation, or a configuration endpoint might accept serialized settings via POST /config and deserialize them with toml::from_str without schema validation.
The risk is heightened in Axum applications that integrate with AI/ML services where model configurations or prompts are serialized and passed between services. If an endpoint accepts serialized LangChain agent configurations or prompt templates without validation, attackers could inject malicious function calls or system prompts, leading to excessive agency or prompt injection as defined in middleBrick's LLM/AI Security checks.
Axum-Specific Detection
Detecting insecure deserialization in Axum requires analyzing both route definitions and custom extractor implementations. middleBrick identifies this by scanning for patterns where user input flows into deserialization sinks without intermediate validation. During its 5–15 second black-box scan, it probes endpoints with payloads designed to trigger deserialization anomalies, such as nested objects, unexpected types, or known gadget chains adapted for Rust serialization formats.
Specifically, middleBrick checks for:
- Endpoints accepting raw bytes (
Bytesextractor) or strings that are later passed toserde_json::from_slice,bincode::deserialize, orpostcard::from_bytes. - Custom extractors that implement
from_requestand call deserialization functions directly on request parts. - Use of
axum::extract::Jsonwith manually extracted payloads (e.g.,let Json(payload) = extract_json(raw_body).await?) where the extraction function does not validate content type or size.
For AI-related endpoints, middleBrick's LLM/AI Security module adds context: it flags deserialization of structures containing tool_calls, function_call, or LangChain-specific fields when the endpoint is unauthenticated, as this could enable excessive agency attacks. The scanner correlates runtime behavior with OpenAPI spec analysis—if a schema defines strict types but the handler accepts serde_json::Value or HashMap<String, serde_json::Value> without validation, it raises a finding.
An example detection scenario: a POST /ai/agent endpoint expecting a serialized LangChain agent configuration. middleBrick sends a payload with a __proto__-like property injection attempt (adapted for Rust) and monitors for changes in response behavior or errors that indicate successful gadget triggering.
Axum-Specific Remediation
Fixing insecure deserialization in Axum centers on strict input validation and using Axum's extractor system correctly. Never deserialize untrusted data directly—instead, rely on Axum's built-in extractors (Json, Form, Query) with strongly typed structs that implement Deserialize. This ensures validation happens at extraction time.
For example, replace unsafe patterns like this:
// UNSAFE: Manual extraction + deserialization
async fn unsafe_handler(raw_body: String) -> impl IntoResponse {
let config: AgentConfig = serde_json::from_str(&raw_body)
.map_err(|e| StatusCode::BAD_REQUEST)?;
// ... use config
}With Axum's native Json extractor:
use axum::extract::Json;
use serde::Deserialize;
#[derive(Deserialize)]
struct AgentConfig {
model: String,
temperature: f32,
tools: Vec,
}
async fn safe_handler(Json(config): Json) -> impl IntoResponse {
// config is already validated and deserialized
// ... use config safely
Json(config)
} When custom extraction is necessary (e.g., for non-JSON formats), validate before deserializing:
use axum::extract::{RequestParts, FromRequest};
use axum::http::header::CONTENT_TYPE;
struct SafeBincode(T);
#[async_trait]
impl FromRequest for SafeBincode
where
T: for<'de> Deserialize<'de>,
S: Send + Sync,
{
type Rejection = (StatusCode, String);
async fn from_request(req: &mut RequestParts) -> Result {
// Validate content type first
if req.headers().get(CONTENT_TYPE) != Some(&"application/octet-stream".parse().unwrap()) {
return Err((StatusCode::UNSUPPORTED_MEDIA_TYPE, "Invalid content type".to_string()));
}
let bytes = Bytes::from_request(req)
.await
.map_err(|e| (StatusCode::BAD_REQUEST, e.to_string()))?;
// Size limit to prevent resource exhaustion
if bytes.len() > 10 * 1024 { // 10KB limit
return Err((StatusCode::PAYLOAD_TOO_LARGE, "Payload too large".to_string()));
}
let value = bincode::deserialize::(&bytes)
.map_err(|e| (StatusCode::BAD_REQUEST, format!("Deserialization failed: {}", e)))?;
Ok(SafeBincode(value))
}
}
// Usage in handler
async fn agent_handler(SafeBincode(config): SafeBincode) -> impl IntoResponse {
// Safe to use
Json(config)
} For AI-specific risks, ensure that deserialized agent configurations cannot contain executable code or unauthorized tool definitions. Validate all fields post-deserialization—e.g., check that tools only contains approved functions, and system_prompt does not contain injection patterns. middleBrick's remediation guidance will flag missing validations in these areas and suggest specific checks based on the detected payload structure.
Frequently Asked Questions
Does middleBrick test for deserialization vulnerabilities in Axum endpoints that use custom extractors?
How does middleBrick differentiate between safe and unsafe deserialization in Axum when reviewing OpenAPI specs?
serde_json::Value or raw bytes without validation, it flags a mismatch. Safe usage shows alignment between spec-defined types and extracted types via Axum's extractors (e.g., Json<TypedStruct>).