Keyboard shortcuts

Press ← or β†’ to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Rullst Security πŸ›‘οΈ

β€œDefense-in-Depth RASP, Cryptographic Vault & Runtime Protection for Rust”

rullst-security provides authenticated field encryption, bounded defensive middleware, local abuse controls, and security telemetry. Its RASP/DLP rules are defense-in-depth heuristics: they reduce specific risks but do not establish complete OWASP coverage, replace parameterized SQL/authorization, or certify the application that mounts them. Which security layer to use, and when compares these layers with the Core baseline and gives one recommended production stack.


⚑ Capability & Lifecycle Matrix

SubsystemLifecycle StatusDescription
Rullst Vault🟒 [Implemented]Authenticated AES-256-GCM encryption with 96-bit nonces, AAD, versioned envelopes, and keyring rotation. Key custody remains external.
Bounded RASP🟒 [Implemented: defense in depth]ASCII signature matching plus one decoding pass for URI, headers, and bounded textual/JSON/XML/form bodies. Body media types are classified case-insensitively, with +json/+xml suffixes and prefixed form types, so every body axum’s Json or Form extractor accepts is inspected. A stock PowerShell User-Agent product token (PowerShell/7.4.1, WindowsPowerShell/5.1…) is not treated as the powershell command keyword; execution syntax still blocks. Decoding and body inspection may allocate.
Login Guard Tarpit🟒 [Implemented: local]Progressive delay decisions and bounded, expiring in-memory jails keyed by a hashed identity. The caller performs the returned delay.
Fixed-Window Rate Limiter🟒 [Implemented: local]In-memory limiter keyed from the verified socket peer: per IPv4 address and per IPv6 /64, with IPv4-mapped IPv6 keyed as IPv4. It tracks at most 16,384 keys and does not coordinate multiple processes.
Redis Rate Limiter🟒 [Implemented: feature-gated foundation]redis-rate-limit uses an atomic fixed-window Lua script, namespace validation, hashed client keys and TTL-derived retry metadata. A limiter and its clones share one lazily opened multiplexed connection and reconnect after a connection-level failure instead of connecting per check. Empty/mock_* URLs select an explicit process-local test mode that tracks at most 16,384 client keys and denies new keys while all tracked windows are active; call require_distributed() at production startup. Windows must be at least 1 ms. A live contract proves independent clients share one Redis budget; cluster/failover remains application evidence.
DLP & Secret Masking🟒 [Implemented: bounded]Masks complete PEM private-key blocks (PKCS#8 plain or encrypted, RSA, EC, DSA, OpenSSH and OpenPGP), AWS access-key patterns, and credentials in supported textual database URLs. Textual responses are text/*, JSON, XML (including +xml), YAML and application/javascript. A 206 range that would need masking is withheld with 502 instead of losing its Content-Range; a multipart/byteranges response is checked part by part and withheld the same way when a textual part would need masking or the body cannot be split exactly (at most 256 parts, 8 KiB of headers per part). Each range is inspected on its own. Binary, compressed, streaming, unknown-size, and oversized bodies are not rewritten.
Local SIEM Journals🟒 [Implemented: bounded local]DurableSiemSpool preserves synchronized unsigned SHA-256 frames. The opt-in AuthenticatedSiemSpool adds HMAC-SHA256 sequence/predecessor integrity, one active plus seven historical zeroized keys, byte/record quotas and fail-closed restart/forgery/ordering/external-change behavior. Whole-tail checkpoints, multi-writer coordination, retention, delivery, retry, acknowledgement and external adapters remain operator work.
TOTP Multi-Factor Auth🟒 [Implemented: foundation]Six-digit SHA-1 TOTP generation/verification with a ±1 time-step window, percent-encoded otpauth URI builder, and subject-bound single-use recovery-code verifiers. Enrollment, transactional persistence, rate limits, and account policy remain application concerns.
CSWSH Guard🟒 [Implemented]Exact normalized scheme/host/port validation for WebSocket origins, with a fail-closed default for missing origins. Without an allowlist, the Origin must match the Host (or HTTP/2 :authority) and, when known from a trusted proxy or HTTP/2 :scheme, the request scheme.
HTML Sanitizer🟒 [Implemented: allowlist]ammonia’s default allowlist for user-supplied HTML; sanitize_text escapes for HTML text and quoted attributes only. Allowed links and images can still reference external sites.
JSON Schema Guard🟒 [Implemented: bounded]Rejects malformed, duplicate-key, over-2 MiB and over-32-level JSON; a route-scoped JSON Schema 2020-12 policy checks shape, not business rules. The global guard passes non-JSON media types through.
Honeypot and Decoy Routes🟒 [Implemented: local]Exact trap paths are refused; HoneypotLayer bans the direct peer’s exact IP for 15 minutes by default, deception_trap_middleware only records. Bans are per process and avoided by address rotation.
LLM Prompt Filter🟑 [Implemented: heuristic]Fixed English phrase, chat-token, Markdown-beacon and invisible-character checks on prompt/content/message JSON fields. Rephrased, translated or encoded instructions and indirect injection pass; legitimate text can be refused.
Session Fingerprint (zero_trust)🟑 [Implemented: helper]HMAC of User-Agent, Accept-Language and the client /24 or /64. All inputs are client-controlled or shared; expect false mismatches after browser or network changes. Not a zero-trust architecture.
Timing Guard🟑 [Implemented: coarse]Pads responses to a minimum duration with jitter. Slower paths are not padded and the synthetic CPU work does not match a real password hash.
Canonical Server security stack🟑 [Partial]CSP nonce identity is shared across Core and extended layers, but Core still owns the default Server CSRF/WAF/header/PII stack. Explicit composition is required.
Distributed Rate Limiting Evidence🟑 [Partial]The Redis adapter is implemented, and the two-process deployment acceptance behind a Caddy proxy shares one Redis budget; eviction, failover and trusted-proxy deployment evidence remain required. The legacy no-argument RateLimiter::try_with_distributed still returns DistributedBackendUnsupported rather than guessing configuration.

πŸ” 1. Rullst Vault (Authenticated AES-256-GCM)

FieldEncryptor provides authenticated encryption at rest (AEAD) with a versioned envelope and keyring-assisted rotation. Applications can keep prior keys readable while writing with a new key; deployment coordination, key custody, data re-encryption, and retirement remain operator responsibilities.

Usage Example

use rullst_security::vault::FieldEncryptor;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let master_key = [0x42u8; 32]; // 256-bit cryptographic key
    let sensitive_data = "user_ssn_123-45-6789";

    // Encrypt with key ID and Additional Authenticated Data (AAD)
    let encrypted = FieldEncryptor::encrypt_with_key_id(
        sensitive_data,
        master_key,
        "key-2026-v1",
        b"tenant-organization-id-42",
    )?;
    println!("Ciphertext Envelope: {}", encrypted);

    // Decrypt and verify authentication tag and AAD
    let decrypted = FieldEncryptor::decrypt_with_aad(
        &encrypted,
        master_key,
        b"tenant-organization-id-42",
    )?;
    assert_eq!(decrypted, sensitive_data);

    Ok(())
}

πŸ›‘οΈ 2. Runtime Application Self-Protection (RASP)

The RASP request inspector scrutinizes incoming requests before they reach your controllers:

#![allow(unused)]
fn main() {
use rullst_security::RaspInspector;

let is_sqli = RaspInspector::inspect_text("admin' OR '1'='1");
assert!(is_sqli);

let is_traversal = RaspInspector::inspect_uri("/files/../../../etc/passwd");
assert!(is_traversal);
}

Mount RaspSecurityLayer explicitly when the extended inspector is desired. The default rullst-core::Server currently mounts the smaller Core WAF rather than this layer; consolidation remains roadmap work.

The inspector matches case-insensitive signatures, raw and after one percent-decoding pass, in the request target, every header except Cookie and Authorization, and identity-encoded textual bodies up to 1 MiB. It does not inspect multipart or binary bodies, does not undo double encoding, HTML entities or SQL comments, and refuses ordinary text that contains a signature (../, sleep(, /bin/sh). It reduces exposure to known payloads; it is not an injection defense on its own.


🚫 3. Anti-Bruteforce Login Guard & Tarpit

#![allow(unused)]
fn main() {
use rullst_security::LoginGuard;

async fn apply_login_delay() {
let guard = LoginGuard::new(); // defaults: 5 failures, 15-minute local jail

// Record failure and apply the returned progressive delay in the async handler.
let delay = guard.record_login_failure("account:alice");
tokio::time::sleep(delay).await;

if guard.is_jailed("account:alice") {
    println!("Identity is in the local Login Jail");
}
}
}

πŸ“± 4. Multi-Factor Authentication (TOTP MFA)

#![allow(unused)]
fn main() {
use rullst_security::{
    build_otpauth_uri, generate_mfa_secret, generate_totp_code, verify_totp_code,
};

let secret = generate_mfa_secret();
let otp_uri = build_otpauth_uri("Rullst SaaS", "[email protected]", &secret);
let current_code = generate_totp_code(&secret);

// Validate 6-digit code submitted by user
let is_valid = verify_totp_code(&secret, "123456");
}

The secret must be encrypted at rest. Recovery helpers return plaintext codes only at enrollment and salted HMAC verifiers for storage; consume/delete must be one durable transaction. The application still owns replay/attempt limiting, enrollment confirmation, recovery UX, audit and clock-monitoring policy.