Skip to content

Security

Security is one of Ultimo's two core pillars (with performance). The framework is secure by default, follows defense in depth, and aims to be provable (audited, no unsafe, public disclosure policy).

100% Safe Rust

The framework enforces #![forbid(unsafe_code)] — there is zero unsafe in Ultimo. Combined with Rust's guarantees, that rules out memory-safety bugs (buffer overflows, use-after-free, data races) in the framework itself.

Security headers

Add secure-by-default response headers with one line:

use ultimo::middleware::builtin::security_headers;
 
app.use_middleware(security_headers());

Sets HSTS, X-Content-Type-Options: nosniff, X-Frame-Options: DENY, Referrer-Policy, and a restrictive Permissions-Policy. Content-Security-Policy is opt-in (a wrong CSP breaks more than it protects). Headers are applied only if the handler didn't already set them, so per-route overrides win. Customize:

use ultimo::middleware::builtin::SecurityHeaders;
 
app.use_middleware(
    SecurityHeaders::new()
        .csp("default-src 'self'")
        .frame_options("SAMEORIGIN")
        .build(),
);

Request body-size limit

Cap request bodies to reject oversized payloads (DoS protection) with 413:

app.max_body_size(2 * 1024 * 1024); // 2 MB

On the live server an oversized body is never fully buffered. No limit by default — set one in production.

Client IP & trusted proxies

let ip = ctx.client_ip();   // Option<IpAddr> — best-effort originating client
let peer = ctx.peer_addr(); // Option<SocketAddr> — direct connection peer

By default client_ip() returns the connection peer. Behind a trusted proxy/load balancer, enable header trust so it honors X-Forwarded-For (leftmost) then Forwarded: for=:

app.trust_proxy(true); // ONLY behind a trusted proxy

Sessions & cookies

Cookie-based sessions are secure by default (HttpOnly, Secure, SameSite, 256-bit ids, anti session-fixation, server-side storage). See Sessions.

CSRF protection

The csrf feature provides double-submit cookie CSRF protection. The middleware issues a random token in a (non-HttpOnly) cookie; on unsafe methods (POST/PUT/PATCH/DELETE) the request must echo that token in a header, and the two are compared in constant time (mismatch → 403). Safe methods are exempt.

// Cargo.toml: ultimo = { version = "0.3", features = ["csrf"] }
app.use_middleware(ultimo::csrf::csrf());
 
// or customized:
app.use_middleware(
    ultimo::csrf::Csrf::new()
        .header_name("x-csrf-token")
        .same_site(ultimo::cookie::SameSite::Strict)
        .build(),
);

The frontend reads the csrf_token cookie and sends it back as the x-csrf-token header:

const token = document.cookie.split('; ')
  .find((c) => c.startsWith('csrf_token='))?.split('=')[1];
await fetch('/api/action', { method: 'POST', headers: { 'x-csrf-token': token } });

See the session-auth example for a working login flow with CSRF.

Supply chain

  • cargo audit (RUSTSEC advisories) runs in CI on every PR.
  • cargo deny gates advisories, banned/duplicate deps, and source pinning.
  • Cargo.lock is committed for reproducible builds.
  • cargo-semver-checks guards the public API against accidental breakage.
  • Minimal dependency surface (the WebSocket layer is zero-dependency).

Defense in depth

For production, deploy Ultimo behind a managed WAF/CDN (Cloudflare, AWS WAF, …) for full WAF rules, DDoS mitigation, and geo controls. Ultimo provides the in-app building blocks; the edge handles volumetric and signature-based threats.

Reporting a vulnerability

Please report privately — see SECURITY.md (GitHub Security Advisories). Do not open a public issue for vulnerabilities.

Roadmap

Planned for v0.4.0: auth middleware (JWT / API keys), authorization guards, IP allow/deny, request guards, and geo-blocking hooks. See the roadmap.