Skip to content

Security

Defense-in-depth security architecture.

Overview

flowchart TB
    subgraph Edge["Edge Protection"]
        A[HTTPS/TLS]
        B[Security Headers]
        C[Rate Limiting]
    end

    subgraph Auth["Authentication"]
        D[JWT Tokens]
        E[Refresh Tokens]
        F[Permission Version]
    end

    subgraph Bot["Bot Protection"]
        G[CAPTCHA]
        H[Honeypot]
        I[Blocked Domains]
    end

    subgraph Data["Data Protection"]
        J[Input Validation]
        K[Output Encoding]
        L[SQL Injection Prevention]
    end

    Edge --> Auth
    Auth --> Bot
    Bot --> Data

Security Headers

Applied via SecurityHeadersMiddleware:

Header Value Purpose
Content-Security-Policy Strict policy XSS prevention
X-Content-Type-Options nosniff MIME sniffing prevention
X-Frame-Options DENY Clickjacking prevention
Strict-Transport-Security max-age=31536000 HTTPS enforcement
Referrer-Policy strict-origin-when-cross-origin Referrer control
Permissions-Policy Restricted Feature restrictions
Cross-Origin-Opener-Policy same-origin Window isolation
Cross-Origin-Resource-Policy same-origin Resource sharing

Rate Limiting

Per-endpoint rate limiting via [RateLimit] attribute protects against abuse.

See Middleware for details.

Bot Protection

CAPTCHA (Turnstile)

Cloudflare Turnstile protects public forms:

  • Registration
  • Password reset
  • Contact forms

Invisible challenge for most users.

Honeypot Fields

Hidden form fields that bots fill:

<input
  name="website"
  style={{ display: "none" }}
  tabIndex={-1}
  autoComplete="off"
/>

Requests with filled honeypot are rejected.

Blocked Email Domains

Disposable email domains are blocked at registration.

Configured via Security__Account__BlockedDomains.

Token Security

Access Tokens (JWT)

  • Short-lived (15 minutes default)
  • Contains user ID, role, group ID, permission version
  • Transmitted via Authorization header
  • Validated on every request

Refresh Tokens

  • Long-lived (1-30 days)
  • Stored in HTTP-only, Secure, SameSite cookie
  • Single-use (rotated on refresh)
  • Stored hashed in database

Permission Version

When permissions change (role, group, modules):

  1. User's PermissionVersion increments
  2. Existing tokens have stale version
  3. Middleware rejects stale tokens
  4. Client refreshes token automatically

Input Validation

All input validated via FluentValidation:

  • Type checking
  • Length limits
  • Format validation (email, URL)
  • Business rule validation

See Validation Pattern.

SQL Injection Prevention

  • EF Core parameterized queries
  • No raw SQL with user input
  • DbContext abstractions

Secrets Management

Secret Storage
JWT signing key Environment variable
Database connection Environment variable
API keys Environment variable
User passwords bcrypt hash in database

Never logged, never in source control.

Audit Trail

All entities track:

  • CreatedByUserId
  • CreatedDate
  • UpdatedByUserId
  • UpdatedDate

Soft delete preserves data with DeletedDate.