rust-security
Skill1 starsChanged 6 months agoMITView on GitHub
Rust application security, supply-chain hardening, cryptography hygiene, secrets handling, and secure service patterns. Use when: - User asks about secure Rust code, threat modeling, auth, tokens, password hashing, or secrets - User uses crates like argon2, password-hash, ring, rustls, secrecy, zeroize, jsonwebtoken, or biscuit - User handles untrusted input, file paths, uploads, subprocesses, deserialization, or SSRF-prone URLs - User wants cargo audit, cargo deny, dependency policy, SBOMs, or supply-chain checks - User writes unsafe code exposed to untrusted data or FFI boundaries - User builds web APIs that need TLS, CORS, cookies, sessions, CSRF, rate limits, or tenant isolation - User implements OAuth2/OIDC, API key validation, JWT verification, or mTLS
2 other projects use a version of this.
Adopted by
Most recently taken up by
- po4yka/rust-skillstheir own version, first seen Nov 2, 2026
- JSBtechnologies/rustkbtheir own version, first seen Oct 11, 2026
Other versions2 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 2
- po4yka/rust-skills★ 5same name
- JSBtechnologies/rustkb★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository24 other files it gives its agents.
- rust-api-designSkill
- rust-asyncSkill
- rust-axumSkill
- rust-cliSkill
- rust-cloud-nativeSkill
- rust-concurrencySkill
- rust-databaseSkill
- rust-dioxusSkill
- rust-ecosystemSkill
- rust-embeddedSkill
- rust-error-handlingSkill
- rust-fintechSkill
- rust-masterySkill
- rust-memory-optimizationSkill
- rust-mlSkill
- rust-networkingSkill
- rust-observabilitySkill
- rust-ownershipSkill
- rust-design-patternsSkill
- rust-performanceSkill
- rust-testingSkill
- rust-type-systemSkill
- rust-unsafeSkill
- rust-wasmSkill
The file
We split this skill into the rules it gives an agent
9 rules. About 0 are common, 4 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈4
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 25
Treat all external input as hostile until parsed into domain types.
- Distinctive: line 26
Use reviewed crypto crates and protocols.
- Very specific: line 27
Keep secrets out of Debug, logs, panics, metrics, and error contexts.
- Very specific: line 58
Use secret wrapper types to avoid accidental Debug output.
- Very specific: line 78
Use zeroize for cryptographic material.
- Distinctive: line 97
Use password hashing algorithms for passwords, not fast hashes.
- Very specific: line 127
Always validate exp, iss, and aud.
- Very specific: line 216
Use token-bucket or sliding-window ratelimiters.
- Distinctive: line 297
Do not join user-provided paths directly.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Rust Security
Practical security guidance for Rust services and libraries. Rust removes many memory-safety bugs, but it does not remove auth bugs, logic bugs, injection, weak crypto, dependency risk, or unsafe-code soundness obligations.
Quick Navigation
- references/crypto_secrets.md - password hashing, tokens, TLS, key handling, zeroization
- references/supply_chain.md - cargo audit, cargo deny, dependency policy, SBOMs
Golden Rules
- Treat all external input as hostile until parsed into domain types.
- Use reviewed crypto crates and protocols; do not design cryptography.
- Keep secrets out of
Debug, logs, panics, metrics, and error contexts. - Bound work for untrusted requests: size, depth, time, concurrency, and retries.
- Automate dependency and license policy checks in CI.
Threat Model First
asset: session token
attacker: internet client with many accounts
entry points: login, refresh, logout, API auth middleware
controls: TLS, secure cookies, token rotation, rate limits, audit logs
failure modes: replay, theft from logs, weak signing key, missing tenant checkA short threat model beats scattered hardening. Identify assets, attackers, boundaries, and abuse cases before choosing crates.
Secrets Handling
use secrecy::{ExposeSecret, SecretString};
pub struct Config {
database_url: SecretString,
}
async fn connect(config: &Config) -> Result<(), sqlx::Error> {
sqlx::PgPool::connect(config.database_url.expose_secret()).await?;
Ok(())
}Use secret wrapper types to avoid accidental Debug output. Avoid attaching raw secrets to errors with .context().
Memory Zeroization
use zeroize::{Zeroize, ZeroizeOnDrop, Zeroizing};
// Automatic zeroing on drop
let mut key = Zeroizing::new([0u8; 32]);
key.copy_from_slice(&raw_key);
// Custom types can implement Zeroize
impl Zeroize for SessionKey {
fn zeroize(&mut self) {
self.inner.zeroize();
self.created_at.zeroize();
}
}Use zeroize for cryptographic material. Relying on the OS to reclaim memory is not sufficient.
Password Hashing
use argon2::{Argon2, PasswordHash, PasswordHasher, PasswordVerifier};
use password_hash::{rand_core::OsRng, SaltString};
pub fn hash_password(password: &[u8]) -> Result<String, password_hash::Error> {
let salt = SaltString::generate(&mut OsRng);
Ok(Argon2::default().hash_password(password, &salt)?.to_string())
}
pub fn verify_password(password: &[u8], encoded: &str) -> Result<bool, password_hash::Error> {
let parsed = PasswordHash::new(encoded)?;
Ok(Argon2::default().verify_password(password, &parsed).is_ok())
}Use password hashing algorithms for passwords, not fast hashes. Tune cost parameters for the deployment budget.
JWT Verification
use jsonwebtoken::{decode, DecodingKey, Validation, Algorithm, TokenData};
#[derive(Debug, serde::Deserialize)]
pub struct Claims {
pub sub: String,
pub exp: usize,
pub iss: String,
pub aud: String,
}
pub fn verify_token(token: &str, public_key: &[u8]) -> Result<Claims, jsonwebtoken::errors::Error> {
let mut validation = Validation::new(Algorithm::RS256);
validation.set_audience(&["my-service"]);
validation.iss = Some("auth.my-service.com".to_string());
let token_data: TokenData<Claims> = decode(
token,
&DecodingKey::from_rsa_pem(public_key)?,
&validation,
)?;
Ok(token_data.claims)
}Always validate exp, iss, and aud. Never accept alg: "none". Use asymmetric keys (RS256/ES256) so services verify without holding signing keys.
TLS Configuration
use rustls::ClientConfig;
use rustls_platform_verifier::tls_config;
pub fn tls_client() -> ClientConfig {
// Use the OS-native certificate verifier
tls_config()
.with_client_auth(none) // no client certificate
.expect("tls config")
}Pin a minimum TLS version (1.2 or 1.3) and restrict cipher suites to modern options.
Parse, Validate, Authorize
#[derive(Debug, Clone)]
pub struct TenantId(uuid::Uuid);
pub async fn load_invoice(
auth: &AuthContext,
tenant_id: TenantId,
invoice_id: InvoiceId,
) -> Result<Invoice, Error> {
auth.require_tenant(&tenant_id)?;
repo::find_invoice(tenant_id, invoice_id).await?
.ok_or(Error::NotFound)
}Validate shape at the boundary, then authorize with domain identifiers. Do not rely on UI filtering or hidden route parameters.
Auth Middleware Pattern
use axum::{
extract::{FromRequestParts, Request},
middleware::{from_fn, Next},
response::Response,
};
pub async fn auth_middleware(
mut req: Request,
next: Next,
) -> Result<Response, StatusCode> {
let token = req.headers()
.get("Authorization")
.and_then(|v| v.to_str().ok())
.and_then(|v| v.strip_prefix("Bearer "))
.ok_or(StatusCode::UNAUTHORIZED)?;
let claims = verify_token(token, &PUBLIC_KEY)
.map_err(|_| StatusCode::UNAUTHORIZED)?;
req.extensions_mut().insert(AuthUser {
user_id: claims.sub,
tenant_id: claims.aud,
});
Ok(next.run(req).await)
}Rate Limiting
use governor::{DefaultDirectRateLimiter, Quota, RateLimiter};
use nonzero_ext::nonzero;
use std::num::NonZeroU32;
// Allow 100 requests per second
let limiter = RateLimiter::direct(Quota::per_second(
NonZeroU32::new(100).unwrap(),
));
async fn handle_request() -> Result<(), StatusCode> {
if limiter.check().is_err() {
return Err(StatusCode::TOO_MANY_REQUESTS);
}
// process request
Ok(())
}Use token-bucket or sliding-window ratelimiters. Expose rate limit headers (X-RateLimit-Remaining, Retry-After) for client cooperation.
Input Validation with Bounds
// Use a dedicated validation crate for complex schemas
use validator::{Validate, ValidationError};
#[derive(Debug, Validate, serde::Deserialize)]
pub struct SignupRequest {
#[validate(length(min = 3, max = 100))]
pub username: String,
#[validate(email)]
pub email: String,
#[validate(length(min = 8, max = 256))]
pub password: String,
}Validate at the boundary with explicit constraints. Combine with size limits at the transport layer.
Untrusted Input Bounds
pub fn parse_payload(bytes: &[u8]) -> Result<Event, Error> {
if bytes.len() > 64 * 1024 {
return Err(Error::PayloadTooLarge);
}
let value: serde_json::Value = serde_json::from_slice(bytes)?;
parse_event(value)
}Bound payload sizes, recursion depth, decompression output, regex complexity, and database result sizes.
SSRF Protection
use url::Url;
pub fn validate_outbound_url(raw: &str) -> Result<Url, Error> {
let url = Url::parse(raw)?;
let scheme = url.scheme();
if scheme != "https" && scheme != "http" {
return Err(Error::DisallowedScheme(scheme.to_string()));
}
let host = url.host_str().ok_or(Error::MissingHost)?;
if is_private_ip(host) {
return Err(Error::BlockedPrivateHost(host.to_string()));
}
Ok(url)
}
fn is_private_ip(host: &str) -> bool {
// Parse and check against 10.x.x.x, 172.16-31.x.x, 192.168.x.x, ::1, 127.x.x.x
// Consider using a crate like `is-private` or `ipnet`
false // implementation omitted for brevity
}Prevent SSRF by filtering private IPs, loopback, and metadata endpoints before making outbound HTTP calls.
Path Safety
use camino::{Utf8Path, Utf8PathBuf};
pub fn resolve_upload(root: &Utf8Path, name: &str) -> Result<Utf8PathBuf, Error> {
if name.contains('/') || name.contains('\\') || name == "." || name == ".." {
return Err(Error::InvalidPath);
}
Ok(root.join(name))
}Do not join user-provided paths directly. Prefer opaque IDs and server-generated filenames.
Dependency Supply Chain
# CI steps for every Rust project
cargo audit # known vulnerabilities
cargo deny check # deny list / license policy / advisory DB
cargo sbom --output cyclonedx --format json # SBOM generation# .cargo/deny.toml
[advisories]
vulnerability = "deny"
unmaintained = "warn"
[licenses]
allow = ["MIT", "Apache-2.0", "ISC", "BSD-3-Clause"]
deny = ["GPL-3.0", "AGPL-3.0"]
[bans]
multiple-versions = "deny"
wildcards = "deny"
deny = ["ansi_term"]Anti-Patterns
// Bad: fast hash for password storage.
let digest = sha2::Sha256::digest(password);
// Good: memory-hard password hashing.
let hash = Argon2::default().hash_password(password, &salt)?;// Bad: secret can leak through logs.
#[derive(Debug)]
struct Login { password: String }
// Good: wrapper prevents accidental Debug exposure.
struct Login { password: secrecy::SecretString }// Bad: unchecked token deserialization, no audience/issuer validation.
let claims: Claims = decode_header(token).into();
// Good: full validation with audience, issuer, expiration.
let token = decode::<Claims>(token, &key, &Validation::new(Algorithm::RS256))?;// Bad: comparing secrets with non-constant-time comparison.
if input_token == stored_token { /* ... */ }
// Good: constant-time comparison prevents timing attacks.
use subtle::ConstantTimeEq;
let result = input_token.as_bytes().ct_eq(stored_token.as_bytes());Security Checklist
- Threat model names assets, attackers, trust boundaries, and abuse cases.
- Secrets use wrapper types and are excluded from logs/errors/debug output.
- Passwords use Argon2id/bcrypt/scrypt with per-password salts.
- Tokens have expiration, audience/issuer checks, key rotation plan, and replay controls.
- Untrusted input has explicit size/time/depth bounds.
- Authorization checks use server-side domain identifiers.
- CI runs dependency, license, and advisory checks.
- TLS minimum version 1.2, cipher suites restricted to modern set.
- Rate limiting is applied to authentication and resource-intensive endpoints.
- SSRF protection is in place for all outbound HTTP clients.
- Cryptographic keys use zeroize for memory cleanup.
References
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

