--- paths: - "**/*.rs" --- # Rust Security > This file extends [common/security.md](../common/security.md) with Rust-specific content. ## Secrets Management - Never hardcode API keys, tokens, or credentials in source code - Use environment variables: `std::env::var("API_KEY")` - Fail fast if required secrets are missing at startup - Keep `.env` files in `.gitignore` ```rust // BAD const API_KEY: &str = "sk-abc123..."; // GOOD — environment variable with early validation fn load_api_key() -> anyhow::Result { std::env::var("PAYMENT_API_KEY") .context("PAYMENT_API_KEY must be set") } ``` ## SQL Injection Prevention - Always use parameterized queries — never format user input into SQL strings - Use query builder or ORM (sqlx, diesel, sea-orm) with bind parameters ```rust // BAD — SQL injection via format string let query = format!("SELECT * FROM users WHERE name = '{name}'"); sqlx::query(&query).fetch_one(&pool).await?; // GOOD — parameterized query with sqlx // Placeholder syntax varies by backend: Postgres: $1 | MySQL: ? | SQLite: $1 sqlx::query("SELECT * FROM users WHERE name = $1") .bind(&name) .fetch_one(&pool) .await?; ``` ## Input Validation - Validate all user input at system boundaries before processing - Use the type system to enforce invariants (newtype pattern) - Parse, don't validate — convert unstructured data to typed structs at the boundary - Reject invalid input with clear error messages ```rust // Parse, don't validate — invalid states are unrepresentable pub struct Email(String); impl Email { pub fn parse(input: &str) -> Result { let trimmed = input.trim(); let at_pos = trimmed.find('@') .filter(|&p| p > 0 && p < trimmed.len() - 1) .ok_or_else(|| ValidationError::InvalidEmail(input.to_string()))?; let domain = &trimmed[at_pos + 1..]; if trimmed.len() > 254 || !domain.contains('.') { return Err(ValidationError::InvalidEmail(input.to_string())); } // For production use, prefer a validated email crate (e.g., `email_address`) Ok(Self(trimmed.to_string())) } pub fn as_str(&self) -> &str { &self.0 } } ``` ## Unsafe Code - Minimize `unsafe` blocks — prefer safe abstractions - Every `unsafe` block must have a `// SAFETY:` comment explaining the invariant - Never use `unsafe` to bypass the borrow checker for convenience - Audit all `unsafe` code during review — it is a red flag without justification - Prefer `safe` FFI wrappers around C libraries ```rust // GOOD — safety comment documents ALL required invariants let widget: &Widget = { // SAFETY: `ptr` is non-null, aligned, points to an initialized Widget, // and no mutable references or mutations exist for its lifetime. unsafe { &*ptr } }; // BAD — no safety justification unsafe { &*ptr } ``` ## Dependency Security - Run `cargo audit` to scan for known CVEs in dependencies - Run `cargo deny check` for license and advisory compliance - Use `cargo tree` to audit transitive dependencies - Keep dependencies updated — set up Dependabot or Renovate - Minimize dependency count — evaluate before adding new crates ```bash # Security audit cargo audit # Deny advisories, duplicate versions, and restricted licenses cargo deny check # Inspect dependency tree cargo tree cargo tree -d # Show duplicates only ``` ## Error Messages - Never expose internal paths, stack traces, or database errors in API responses - Log detailed errors server-side; return generic messages to clients - Use `tracing` or `log` for structured server-side logging ```rust // Map errors to appropriate status codes and generic messages // (Example uses axum; adapt the response type to your framework) match order_service.find_by_id(id) { Ok(order) => Ok((StatusCode::OK, Json(order))), Err(ServiceError::NotFound(_)) => { tracing::info!(order_id = id, "order not found"); Err((StatusCode::NOT_FOUND, "Resource not found")) } Err(e) => { tracing::error!(order_id = id, error = %e, "unexpected error"); Err((StatusCode::INTERNAL_SERVER_ERROR, "Internal server error")) } } ``` ## References See skill: `rust-patterns` for unsafe code guidelines and ownership patterns. See skill: `security-review` for general security checklists.