Vortos
Security

Security

Enterprise-grade HTTP-layer and application-layer security for Vortos — headers, CORS, CSRF, IP filtering, request signing, password policy, encryption, secrets management, and data masking.

Security

vortos-security is the HTTP-layer security module for the Vortos framework. It covers everything a production API needs before a single line of your domain code runs: security headers, CORS preflight, CSRF protection, IP filtering, webhook signature verification, password policy enforcement, field-level encryption, secrets management, and PII masking in logs.

Compile-Time First

Every attribute (#[SkipCsrf], #[AllowIp], #[RequiresSignature], #[Encrypted]) is scanned by a compiler pass at container build time. At runtime each middleware does a single array lookup — zero reflection, O(1) cost regardless of how many controllers you have.

Middleware Chain

All security middleware runs in the outermost layers of the request pipeline — before authentication, authorization, or any domain logic:

Inbound Request

    ▼ priority 100: SecurityHeadersMiddleware   — adds security headers to every response

    ▼ priority 95:  CorsMiddleware              — preflight (OPTIONS → 204) + CORS headers

    ▼ priority 90:  IpFilterMiddleware          — deny/allow by IP or CIDR range

    ▼ priority 85:  CsrfMiddleware              — double-submit cookie validation

    ▼ priority 75:  RequestSignatureMiddleware  — HMAC-SHA256 webhook verification

    ▼ priority 7:   RateLimitMiddleware (IP)    — [Auth module]

    ▼ priority 6:   AuthMiddleware              — [Auth module]

    ▼ priority 3:   AuthorizationMiddleware     — [Authorization module]

    ▼               Your controller

Feature Overview

Installation

vortos-security is auto-discovered from the monorepo path repository. In a standalone project:

composer require vortos/vortos-security

No manual registration is needed — SecurityPackage is discovered via extra.vortos.package in its composer.json.

Configuration

All security settings live in config/security.php. Per-environment overrides go in config/dev/security.php and config/prod/security.php — they are loaded after the base file and any value set there wins.

config/security.php
use Vortos\Security\DependencyInjection\VortosSecurityConfig;

return static function (VortosSecurityConfig $config): void {
    $config->headers()
        ->xFrameOptions('DENY')
        ->xContentTypeOptions(true)
        ->referrerPolicy('strict-origin-when-cross-origin');

    $config->cors()
        ->origins(['https://app.example.com'])
        ->methods(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS']);

    $config->csrf()->enabled(true);
    $config->ipFilter()->trustedProxies(['127.0.0.1', '::1']);

    $config->passwordPolicy()
        ->minLength(12)
        ->requireUppercase(true)
        ->requireDigit(true)
        ->requireSpecial(true)
        ->checkCommonPasswords(true);
};
config/dev/security.php
return static function (VortosSecurityConfig $config): void {
    $config->headers()->hsts(false);
    $config->cors()->origins(['*']);
    $config->csrf()->enabled(false); // disable for Postman / API testing
    $config->ipFilter()->enabled(false);
};
config/prod/security.php
return static function (VortosSecurityConfig $config): void {
    $config->headers()
        ->hsts(maxAge: 31536000, includeSubDomains: true, preload: true)
        ->csp()->defaultSrc("'self'")->scriptSrc("'self'")->reportUri('/csp-report');

    $config->cors()->origins(['https://app.example.com']);
    $config->csrf()->enabled(true)->cookieSecure(true);
    $config->ipFilter()->enabled(true);
};

Opt-In vs Always-On

FeatureDefaultNotes
Security HeadersAlways onZero-overhead — header map built at compile time
CORSAlways onConfigure origins([]) to disable effectively
CSRFAlways onCan be disabled per env or per route via #[SkipCsrf]
IP FilteringAlways onNo-op unless rules are configured
Request SigningAlways onNo-op on routes without #[RequiresSignature]
Password PolicyAlways onCall PasswordPolicyService::validate() explicitly
EncryptionOpt-inSet $config->encryption()->enabled(true)
Secrets ManagementAlways onDefault: EnvSecretsProvider (zero overhead)
Data MaskingOpt-inSet $config->dataMasking()->enabled(true)
Security Event BusAlways onOnly fires when a violation actually occurs

On this page