Skip to content

Security

Valerio edited this page Apr 28, 2026 · 4 revisions

Security

Harpyx is designed to host sensitive customer documents, so security is treated as a cross-cutting concern — authentication, authorization, upload validation, malware scanning, encryption at rest, secret handling, and audit are all first-class subsystems rather than afterthoughts.

This page focuses on the content-security and secret-handling concerns. For the identity and role model, see Authentication and Authorization.

For operational database and object-storage encryption setup, including SQL Server TDE, SQL TLS, MinIO/object-storage encryption, and backup handling, see Data Encryption. For EU data-protection governance, hosting-region choices, subprocessors, DPIA, retention, and breach-response planning, see GDPR Compliance.

Upload security

Every uploaded file passes through a three-stage gate before it is accepted:

1. Policy check (IUploadSecurityPolicyService)

Validates extension and MIME type against UploadSecurity:AllowedExtensions / UploadSecurity:AllowedContentTypes in appsettings.json. Unexpected formats are rejected immediately, before any bytes are persisted. The allowlist is intentionally explicit rather than using a denylist — new formats need an explicit opt-in.

2. Malware scan (IFileMalwareScannerClamAvFileMalwareScanner)

When MalwareScan:Enabled=true, uploads are streamed through ClamAV's clamd over TCP (MalwareScan:Host:Port). The scanner runs in fail-closed mode by default — if the scanner is unreachable or times out (MalwareScan:TimeoutSeconds), the upload is rejected. Administrators who need fail-open behavior in non-production must flip MalwareScan:FailClosed explicitly.

Scan outcomes:

Outcome Document state Behavior
Clean Pending Proceeds to ingestion as usual
Infected Quarantined Enqueued job is skipped with job_skipped_security
Scanner unreachable + fail-closed Rejected Upload rejected outright, never persisted

First container startup can take longer while ClamAV downloads virus signatures; subsequent scans are fast.

3. Size / streaming limits

The WebApp reads uploads as streams and honors ASP.NET Core's request-size limits. URL-based ingestion (UrlFetcherService) additionally enforces UrlFetch:TimeoutSeconds, UrlFetch:MaxContentBytes, and UrlFetch:AllowHttp (default false) to prevent runaway fetches or accidental plain-HTTP egress.

Encryption at rest

Harpyx uses layered encryption controls. Application-level encryption protects selected secrets, while database, object-storage, disk, and backup encryption are operational controls configured on the infrastructure. The deployment checklist for those controls lives in Data Encryption.

User LLM API keys

AesEncryptionService encrypts BYO and hosted LLM credentials using AES-256-GCM before persistence. The master key is read from Encryption:MasterKey (a base64-encoded 32-byte key) and stored only in process memory. Rotating the master key requires re-encrypting existing LlmConnection rows that contain encrypted API keys.

The plaintext key is never logged, never serialized to telemetry, and never returned from the API to clients.

ASP.NET Data Protection

Cookies and antiforgery tokens are protected by ASP.NET Data Protection. In production, keys are persisted to the filesystem under DataProtection:KeysPath (default App_Data/keys). For multi-instance deployments, this path should point to a shared volume so all instances decrypt each other's cookies. DataProtection:ApplicationName (Harpyx) ensures key rings are not accidentally shared across applications.

Secret handling

  • No secrets in source control. .env is in .gitignore; .env.example is the template.
  • Uniform override chain. Secrets enter through environment variables in dev and Azure Key Vault in production, hitting the same configuration keys — see Configuration.
  • Admin bootstrap is explicit. SeedOptions:AdminUsers seeds known platform admins by provider-specific stable id; keep this list small and manage day-to-day role changes through the admin UI.

Audit

All security-relevant events are persisted as append-only AuditEvent rows via IAuditService.RecordAsync. Current event types:

Event Fired on
login Successful authentication
access_denied Authenticated user not on the allowlist
upload File accepted by the upload pipeline
job_start Worker picks up a ParseDocumentJob
job_end Job completes successfully
job_error Job fails with an exception
job_skipped_security Job bypassed because the document was quarantined or rejected

Because every subsystem funnels through IAuditService, the audit trail is uniform across WebApp and Worker.

HTTP-level hardening

Configured in AddHarpyxWebAppServices and the pipeline extensions:

  • Forwarded headersForwardedHeaders.XForwardedFor | XForwardedProto with KnownProxies / KnownNetworks allowlists. ReverseProxy:ForwardLimit prevents spoofed chains.
  • CSRFAutoValidateAntiforgeryTokenAttribute applied globally to controllers.
  • Rate limiting — two fixed-window policies: api (120 req/min per IP) and auth (12 req/min per IP). Responses include Retry-After headers; rejections return RFC 7807 Problem Details.
  • Problem Details — all error responses carry traceId to correlate user-visible errors with the server-side trace.

Threat assumptions

Harpyx is designed against:

  • Untrusted upload content (malicious files, zip bombs, malformed PDFs) — mitigated by ClamAV, extension/MIME allowlists, library-level hardening (PdfPig, OpenXml, SharpCompress), and size/timeout limits.
  • Opportunistic cookie theft — mitigated by Data Protection, cookie hardening defaults, and CSRF.
  • Rate-limit / brute-force attacks against auth endpoints — mitigated by the auth rate-limit policy.
  • Accidental secret exposure — mitigated by the override chain and the zero-secret .env.example.
  • Insider access beyond tenant scope — mitigated by ITenantScopeService + allowlist + audit.

It does not defend against a compromised host running the WebApp/Worker processes: a root-level attacker on the host has access to the Data Protection keys and the in-memory encryption master key. Defending against that level of threat requires host-level controls (hardware TPMs, HSM-backed Key Vault, confidential computing) outside Harpyx's scope.

Clone this wiki locally