Security Middleware

Comprehensive security middleware for Armature - inspired by Helmet for Express.js.

Table of Contents

Overview

The Armature Security middleware provides a collection of security headers and protections that help secure your web applications against common vulnerabilities. It's inspired by Helmet for Express.js and provides similar functionality for Rust/Armature applications.

Features

โœ… Content Security Policy (CSP) - Prevent XSS attacks โœ… HTTP Strict Transport Security (HSTS) - Force HTTPS โœ… X-Frame-Options - Prevent clickjacking โœ… X-Content-Type-Options - Prevent MIME sniffing โœ… X-XSS-Protection - Enable browser XSS filters โœ… Referrer Policy - Control referrer information โœ… DNS Prefetch Control - Control DNS prefetching โœ… Expect-CT - Certificate Transparency โœ… X-Download-Options - Prevent IE download execution โœ… X-Permitted-Cross-Domain-Policies - Control Flash/PDF policies โœ… Hide X-Powered-By - Remove server fingerprinting

Installation

Add to your Cargo.toml:

[dependencies]
armature-framework = { version = "0.1", features = ["security"] }

Quick Start

Adding to Middleware Chain (Recommended)

The preferred way to use security middleware is to add it to your application's middleware chain:

use armature_framework::prelude::*;
use armature_framework::{MiddlewareChain, SecurityHeadersMiddleware};
use armature_security::SecurityMiddleware;

// Create your middleware chain
let mut middleware_chain = MiddlewareChain::new();

// Add security middleware (recommended to be early in the chain)
middleware_chain.use_middleware(SecurityMiddleware::default());

// Add other middleware
middleware_chain.use_middleware(SecurityHeadersMiddleware::new());

// Define your module
#[module(
    providers: [MyService],
    controllers: [MyController]
)]
#[derive(Default)]
struct AppModule;

Full Application Example

use armature_framework::prelude::*;
use armature_framework::{MiddlewareChain, LoggerMiddleware, CorsMiddleware};
use armature_security::{
    SecurityMiddleware,
    content_security_policy::CspConfig,
    hsts::HstsConfig,
    frame_guard::FrameGuard,
};

#[injectable]
#[derive(Clone, Default)]
struct ApiService;

#[controller("/api")]
#[derive(Default, Clone)]
struct ApiController;

impl ApiController {
    #[get("/data")]
    async fn get_data() -> Result<Json<serde_json::Value>, Error> {
        Ok(Json(serde_json::json!({ "status": "ok" })))
    }
}

#[module(
    providers: [ApiService],
    controllers: [ApiController]
)]
#[derive(Default)]
struct AppModule;

#[tokio::main]
async fn main() {
    // Build middleware chain with security
    let mut middleware_chain = MiddlewareChain::new();
    
    // 1. Logging (first to capture all requests)
    middleware_chain.use_middleware(LoggerMiddleware::new());
    
    // 2. CORS (handle preflight early)
    middleware_chain.use_middleware(
        CorsMiddleware::new()
            .allow_origin("https://example.com")
            .allow_credentials(true)
    );
    
    // 3. Security headers (apply to all responses)
    middleware_chain.use_middleware(
        SecurityMiddleware::new()
            .with_hsts(HstsConfig::new(31536000).include_subdomains(true))
            .with_frame_guard(FrameGuard::Deny)
            .with_csp(CspConfig::new().default_src(vec!["'self'".to_string()]))
            .hide_powered_by(true)
    );
    
    println!("Server running with security middleware enabled");
}

Custom Configuration

use armature_security::{
    SecurityMiddleware,
    content_security_policy::CspConfig,
    hsts::HstsConfig,
    frame_guard::FrameGuard,
    referrer_policy::ReferrerPolicy,
};

let security = SecurityMiddleware::new()
    .with_csp(CspConfig::default())
    .with_hsts(HstsConfig::new(31536000)) // 1 year
    .with_frame_guard(FrameGuard::Deny)
    .with_referrer_policy(ReferrerPolicy::NoReferrer)
    .hide_powered_by(true);

// Add to middleware chain
middleware_chain.use_middleware(security);

Configuration

Enable All Features (Recommended)

// All security features with 1-year HSTS
let security = SecurityMiddleware::enable_all(31536000);

Start from Scratch

// No protections - configure manually
let security = SecurityMiddleware::new()
    .with_frame_guard(FrameGuard::SameOrigin)
    .hide_powered_by(true);

Security Headers

Content Security Policy (CSP)

Prevents XSS attacks by declaring which dynamic resources are allowed to load.

use armature_security::content_security_policy::CspConfig;

let csp = CspConfig::new()
    .default_src(vec!["'self'".to_string()])
    .script_src(vec![
        "'self'".to_string(),
        "https://cdn.example.com".to_string()
    ])
    .style_src(vec![
        "'self'".to_string(),
        "'unsafe-inline'".to_string()
    ])
    .img_src(vec![
        "'self'".to_string(),
        "data:".to_string(),
        "https:".to_string()
    ]);

let security = SecurityMiddleware::new().with_csp(csp);

Output Header:

Content-Security-Policy: default-src 'self'; script-src 'self' https://cdn.example.com; ...

HTTP Strict Transport Security (HSTS)

Forces browsers to use HTTPS.

use armature_security::hsts::HstsConfig;

let hsts = HstsConfig::new(31536000) // 1 year in seconds
    .include_subdomains(true)
    .preload(true);

let security = SecurityMiddleware::new().with_hsts(hsts);

Output Header:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

X-Frame-Options

Prevents clickjacking by controlling if your site can be framed.

use armature_security::frame_guard::FrameGuard;

// Deny all framing
let security = SecurityMiddleware::new()
    .with_frame_guard(FrameGuard::Deny);

// Allow same origin
let security = SecurityMiddleware::new()
    .with_frame_guard(FrameGuard::SameOrigin);

// Allow specific origin
let security = SecurityMiddleware::new()
    .with_frame_guard(FrameGuard::AllowFrom("https://example.com".to_string()));

Output Header:

X-Frame-Options: DENY
X-Frame-Options: SAMEORIGIN
X-Frame-Options: ALLOW-FROM https://example.com

Referrer Policy

Controls how much referrer information is sent with requests.

use armature_security::referrer_policy::ReferrerPolicy;

let security = SecurityMiddleware::new()
    .with_referrer_policy(ReferrerPolicy::NoReferrer);

// Other options:
// - NoReferrer
// - NoReferrerWhenDowngrade
// - Origin
// - OriginWhenCrossOrigin
// - SameOrigin
// - StrictOrigin
// - StrictOriginWhenCrossOrigin
// - UnsafeUrl

Output Header:

Referrer-Policy: no-referrer

X-XSS-Protection

Enables the browser's XSS filtering.

use armature_security::xss_filter::XssFilter;

// Enable with blocking
let security = SecurityMiddleware::new()
    .with_xss_filter(XssFilter::EnabledBlock);

// Just enable (don't block)
let security = SecurityMiddleware::new()
    .with_xss_filter(XssFilter::Enabled);

// Disable
let security = SecurityMiddleware::new()
    .with_xss_filter(XssFilter::Disabled);

Output Header:

X-XSS-Protection: 1; mode=block

DNS Prefetch Control

Controls browser DNS prefetching.

use armature_security::dns_prefetch_control::DnsPrefetchControl;

// Disable DNS prefetching (more privacy)
let security = SecurityMiddleware::new()
    .with_dns_prefetch_control(DnsPrefetchControl::Off);

// Enable DNS prefetching (better performance)
let security = SecurityMiddleware::new()
    .with_dns_prefetch_control(DnsPrefetchControl::On);

Output Header:

X-DNS-Prefetch-Control: off

Expect-CT

Helps detect misissued certificates.

use armature_security::expect_ct::ExpectCtConfig;

let expect_ct = ExpectCtConfig::new(86400) // 1 day
    .enforce(true)
    .report_uri("https://example.com/report".to_string());

let security = SecurityMiddleware::new().with_expect_ct(expect_ct);

Output Header:

Expect-CT: max-age=86400, enforce, report-uri="https://example.com/report"

Hide X-Powered-By

Removes the X-Powered-By header to prevent server fingerprinting.

let security = SecurityMiddleware::new()
    .hide_powered_by(true);

Result: X-Powered-By header is removed from responses.

Best Practices

1. Use Default Settings for Production

// Recommended for most applications
let security = SecurityMiddleware::default();

2. Customize for Your Needs

// Example: API server with custom CSP
let security = SecurityMiddleware::new()
    .with_csp(
        CspConfig::new()
            .default_src(vec!["'self'".to_string()])
            .connect_src(vec!["'self'".to_string(), "https://api.example.com".to_string()])
    )
    .with_hsts(HstsConfig::new(31536000))
    .hide_powered_by(true);

3. Test in Development

Use browser developer tools to verify headers are applied:

# Chrome/Firefox: Network tab โ†’ Select request โ†’ Headers section

4. HSTS Considerations

  • Start with a shorter max-age (e.g., 300 seconds) in testing
  • Gradually increase to 1 year (31536000 seconds) in production
  • Only enable preload after testing thoroughly

5. CSP Development

  • Start with report-only mode:
    let csp = CspConfig::default().report_only(true);
    
  • Monitor violations before enforcing
  • Gradually tighten policies

API Reference

SecurityMiddleware

Main security middleware struct.

Methods

  • new() - Create with no protections
  • default() - Create with recommended settings
  • enable_all(max_age: u64) - Enable all protections
  • with_csp(config: CspConfig) - Add CSP
  • with_hsts(config: HstsConfig) - Add HSTS
  • with_frame_guard(guard: FrameGuard) - Set frame options
  • with_referrer_policy(policy: ReferrerPolicy) - Set referrer policy
  • with_xss_filter(filter: XssFilter) - Set XSS filter
  • with_dns_prefetch_control(control: DnsPrefetchControl) - Control DNS prefetch
  • with_expect_ct(config: ExpectCtConfig) - Add Expect-CT
  • hide_powered_by(hide: bool) - Hide X-Powered-By header
  • apply(response: HttpResponse) -> HttpResponse - Apply headers to response

Submodules

  • content_security_policy - CSP configuration
  • hsts - HSTS configuration
  • frame_guard - X-Frame-Options
  • referrer_policy - Referrer-Policy
  • xss_filter - X-XSS-Protection
  • dns_prefetch_control - X-DNS-Prefetch-Control
  • expect_ct - Expect-CT
  • content_type_options - X-Content-Type-Options
  • download_options - X-Download-Options
  • permitted_cross_domain_policies - X-Permitted-Cross-Domain-Policies

Examples

Complete Application with Middleware Chain

use armature_framework::prelude::*;
use armature_framework::{
    MiddlewareChain, LoggerMiddleware, CorsMiddleware, 
    RequestIdMiddleware, TimeoutMiddleware
};
use armature_security::{
    SecurityMiddleware,
    content_security_policy::CspConfig,
    hsts::HstsConfig,
    frame_guard::FrameGuard,
    referrer_policy::ReferrerPolicy,
};

#[injectable]
#[derive(Clone, Default)]
struct UserService;

impl UserService {
    fn get_users(&self) -> Vec<String> {
        vec!["Alice".to_string(), "Bob".to_string()]
    }
}

#[controller("/api/users")]
#[derive(Default, Clone)]
struct UserController;

impl UserController {
    #[get("/")]
    async fn list_users() -> Result<Json<Vec<String>>, Error> {
        let service = UserService::default();
        Ok(Json(service.get_users()))
    }
    
    #[get("/:id")]
    async fn get_user(req: HttpRequest) -> Result<Json<String>, Error> {
        let id = req.params.get("id").unwrap_or(&"0".to_string()).clone();
        Ok(Json(format!("User {}", id)))
    }
}

#[module(
    providers: [UserService],
    controllers: [UserController]
)]
#[derive(Default)]
struct AppModule;

#[tokio::main]
async fn main() {
    println!("๐Ÿ”’ Secure API Server");
    println!("====================\n");
    
    // Build comprehensive middleware stack
    let mut middleware = MiddlewareChain::new();
    
    // Request tracking
    middleware.use_middleware(RequestIdMiddleware);
    middleware.use_middleware(LoggerMiddleware::new());
    
    // CORS for cross-origin requests
    middleware.use_middleware(
        CorsMiddleware::new()
            .allow_origin("https://myapp.com")
            .allow_methods("GET, POST, PUT, DELETE")
            .allow_headers("Content-Type, Authorization")
            .allow_credentials(true)
    );
    
    // Security headers - comprehensive protection
    middleware.use_middleware(
        SecurityMiddleware::new()
            .with_hsts(
                HstsConfig::new(31536000)
                    .include_subdomains(true)
                    .preload(true)
            )
            .with_frame_guard(FrameGuard::Deny)
            .with_referrer_policy(ReferrerPolicy::StrictOriginWhenCrossOrigin)
            .with_csp(
                CspConfig::new()
                    .default_src(vec!["'self'".to_string()])
                    .script_src(vec!["'self'".to_string()])
                    .style_src(vec!["'self'".to_string(), "'unsafe-inline'".to_string()])
                    .img_src(vec!["'self'".to_string(), "data:".to_string(), "https:".to_string()])
                    .connect_src(vec!["'self'".to_string(), "https://api.example.com".to_string()])
            )
            .hide_powered_by(true)
    );
    
    // Request timeout
    middleware.use_middleware(TimeoutMiddleware::new(30));
    
    println!("Middleware stack configured:");
    println!("  โœ“ Request ID tracking");
    println!("  โœ“ Request logging");
    println!("  โœ“ CORS protection");
    println!("  โœ“ Security headers (HSTS, CSP, Frame-Options, etc.)");
    println!("  โœ“ 30 second timeout");
    println!();
    println!("Server running on http://localhost:3000");
}

API-Only Configuration (Minimal)

use armature_framework::prelude::*;
use armature_framework::MiddlewareChain;
use armature_security::{SecurityMiddleware, frame_guard::FrameGuard};

// For API servers that don't serve HTML, use a minimal config
let mut middleware = MiddlewareChain::new();

middleware.use_middleware(
    SecurityMiddleware::new()
        .with_frame_guard(FrameGuard::Deny)  // Prevent embedding
        .hide_powered_by(true)                // Hide server info
        // Skip CSP for API-only servers
);

Development vs Production

use armature_security::{SecurityMiddleware, hsts::HstsConfig};

fn create_security_middleware(is_production: bool) -> SecurityMiddleware {
    if is_production {
        // Full security for production
        SecurityMiddleware::new()
            .with_hsts(HstsConfig::new(31536000).preload(true))
            .hide_powered_by(true)
    } else {
        // Relaxed for development
        SecurityMiddleware::new()
            .with_hsts(HstsConfig::new(300)) // Short HSTS for testing
            .hide_powered_by(false)          // Keep for debugging
    }
}

// Usage
let is_prod = std::env::var("ENVIRONMENT").map(|v| v == "production").unwrap_or(false);
middleware_chain.use_middleware(create_security_middleware(is_prod));

Summary

The Armature Security middleware provides comprehensive protection against common web vulnerabilities:

  • โœ… Easy to use - SecurityMiddleware::default() for most cases
  • โœ… Highly customizable - Configure each feature individually
  • โœ… Production-ready - Based on industry best practices
  • โœ… Well-tested - 21+ unit tests covering all features
  • โœ… Type-safe - Full Rust type safety

For more examples, see examples/security_example.rs in the repository.