Excessive Data Exposure in Axum with Jwt Tokens
Excessive Data Exposure in Axum with Jwt Tokens — how this specific combination creates or exposes the vulnerability
Excessive Data Exposure occurs when an API returns more information than necessary for a given operation, and in Axum this risk is heightened when JWT tokens are handled without strict data scoping. In Rust services built with Axum, developers often embed user roles, permissions, email addresses, or internal identifiers directly into the JWT payload. Because JWTs are typically sent in the Authorization header and may also be stored client-side, any endpoint that returns the token or echoes decoded claims without filtering can unintentionally expose sensitive fields.
For example, an authentication handler might issue a JWT containing a user’s role and a unique internal user ID, and a profile endpoint might return the full decoded token alongside other profile details. If that endpoint lacks explicit field selection or redaction, the response can reveal the user ID, role, or even scopes that should be hidden from the client. Attackers can leverage this Excessive Data Exposure by capturing the token and using the exposed identifiers to probe other endpoints, such as BOLA/IDOR-prone routes that reference the same user ID. When JWTs include sensitive claims and Axum endpoints reflect those claims without validation or minimization, the API widens the attack surface.
Another scenario specific to Axum involves middleware that decodes JWTs for authorization and then inadvertently passes the entire claims set to downstream handlers or logging mechanisms. If logs or error responses include the full claims map, sensitive data such as roles or scopes can be exposed to unauthorized parties with log access. This pattern is common when developers use generic claim extraction utilities that return the whole JSON object instead of selecting only the required keys. Because Axum allows fine-grained control over request extensions and handlers, failing to sanitize the claims before propagation results in Excessive Data Exposure across multiple layers of the application.
In the context of the 12 security checks run by middleBrick, Excessive Data Exposure is flagged when responses include tokens or decoded JWT claims that contain high-sensitivity fields. The scanner tests unauthenticated endpoints that reflect token data and examines whether personally identifiable information, roles, or internal identifiers are returned without need-to-know justification. This is especially relevant for endpoints that integrate LLM components, where verbose or unredacted outputs might further amplify exposure. middleBrick’s checks highlight these risks and provide remediation guidance focused on minimizing data in both token issuance and response payloads.
Jwt Tokens-Specific Remediation in Axum — concrete code fixes
To remediate Excessive Data Exposure when using JWT tokens in Axum, you should limit the claims stored in the token and strictly control what data is returned from endpoints. Below are concrete code examples that demonstrate secure practices.
First, issue JWTs with only necessary, non-sensitive claims. Avoid embedding roles or IDs unless absolutely required, and prefer opaque tokens or reference tokens for sensitive contexts. When you must include claims, restrict them to a minimal set such as user ID and a short-lived scope.
// Cargo.toml dependencies: jsonwebtoken = "0.5", serde = { version = "1.0", features = ["derive"] }
use jsonwebtoken::{encode, decode, Header, Validation, EncodingKey, DecodingKey};
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String, // user id, minimal
scope: String, // limited scope, e.g., "read:profile"
exp: usize, // short expiration
}
fn issue_token(user_id: &str) -> String {
let claims = Claims {
sub: user_id.to_string(),
scope: "read:profile".to_string(),
exp: (chrono::Utc::now() + chrono::Duration::minutes(15)).timestamp() as usize,
};
encode(&Header::default(), &claims, &EncodingKey::from_secret(b"secret_key_32_bytes_long_!!"))
.expect("Failed to encode token")
}
Second, ensure Axum handlers do not return the full JWT or all decoded claims. Instead, construct response DTOs that include only what the client needs. Use selective serialization to omit sensitive fields.
use axum::{routing::get, Router, response::Json};
use serde::Serialize;
#[derive(Serialize)]
struct ProfileResponse {
user_id: String,
// do not include roles or scopes unless necessary
}
async fn profile_handler(
Extension(claims): Extension, // claims already validated and filtered
) -> Json {
let response = ProfileResponse {
user_id: claims.sub,
};
Json(response)
}
fn app() -> Router {
Router::new()
.route("/profile", get(profile_handler))
}
Third, apply middleware that strips unnecessary claims before they reach handlers. Store only a sanitized subset in request extensions, and avoid passing the raw decoded token map.
use axum::{extract::Extension, async_trait, middleware::Next, response::Response};
use std::convert::Infallible;
use tower_http::auth::Authorization;
struct SanitizedClaims {
pub user_id: String,
}
pub async fn claims_middleware(
Authorization(token): Authorization<Bearer>,
mut next: Next<("body", String)>,
) -> Result<Response, Infallible> {
// decode and validate token
let token_data = decode::<Claims>(
&token.0, &DecodingKey::from_secret(b"secret_key_32_bytes_long_!!"),
&Validation::new(jsonwebtoken::Algorithm::HS256),
).expect("Invalid token");
let sanitized = SanitizedClaims {
user_id: token_data.claims.sub,
};
next.run(Extension(sanitized)).await
}
Finally, configure logging to exclude raw claims and ensure error responses do not echo token content. Combine these practices with middleBrick’s scans to verify that endpoints no longer expose excessive data and that JWT handling aligns with secure design principles.
Related CWEs: propertyAuthorization
| CWE ID | Name | Severity |
|---|---|---|
| CWE-915 | Mass Assignment | HIGH |