Application Security

Technical overview of DFIRe's security architecture, including authentication, encryption at rest, and role-based access controls.

Security Overview

DFIRe is designed for handling sensitive forensic and incident response data. The application implements defense-in-depth with multiple security layers.

Feature Implementation
Authentication Session-based with OIDC SSO support
File Encryption AES-256-GCM with three-layer key hierarchy
Credential Storage Fernet encryption (AES-128-CBC + HMAC-SHA256)
Access Control Two-tier RBAC (global + case-scoped)
Session Management Database-backed with O(1) revocation
CSRF Protection Double-submit cookie pattern
Rate Limiting Configurable per-endpoint throttling
Audit Logging Immutable, fail-closed audit trail

Infrastructure Security Responsibility

Important: The security features documented on this page secure the DFIRe application itself. You are solely responsible for securing your underlying infrastructure, including the database, file storage, and network access.

DFIRe is designed as a user interface layer that operates on data you own and control. When deployed for production, DFIRe does not include a database or file storage system - you must provide these yourself. This separation is intentional: database security requirements vary significantly between organizations, and DFIRe allows you to choose the solution that fits your needs - whether that's an on-premise PostgreSQL cluster, a managed Database-as-a-Service, or another configuration.

What DFIRe Secures

  • Application-layer authentication and session management
  • Role-based access control within the application
  • Encryption of file attachments before storage
  • Encryption of stored credentials (API keys, SSO secrets)
  • CSRF protection and rate limiting
  • Audit logging of user actions

What You Must Secure

Component Your Responsibility
PostgreSQL Database Access control, authentication, encryption at rest, backups, replication, network isolation. Case data is stored in plaintext in the database for performance and full-text search capability.
File Storage Access control, redundancy, backups. Files are encrypted by DFIRe before storage, but you control the storage infrastructure.
Network Access Firewall rules, TLS termination, VPN/private network configuration, load balancing, DDoS protection.
Server Infrastructure Operating system security, container runtime security, resource limits, monitoring, patching.
Backup & Recovery Database backups, file storage backups, encryption key backups, disaster recovery procedures.
Compliance Ensuring the data you store complies with applicable regulations (GDPR, HIPAA, etc.) and that your infrastructure meets compliance requirements.

High-Security Environments

For organizations with strict security requirements:

  • Air-gapped deployment: DFIRe supports offline licensing for networks without internet access
  • Network isolation: Deploy DFIRe in a dedicated VLAN or private network segment
  • Database encryption: Enable PostgreSQL's Transparent Data Encryption (TDE) or use encrypted storage volumes
  • Access logging: Configure database audit logging in addition to DFIRe's application-level audit trail
  • Zero-trust architecture: Require VPN or identity-aware proxy for all access to DFIRe

See the End User License Agreement included with DFIRe for the complete terms regarding infrastructure and data security responsibilities.

Authentication

DFIRe uses session-based authentication with support for both local credentials and federated identity via OpenID Connect (OIDC).

Session Management

Sessions are managed using Django's database-backed session framework with additional tracking for security:

  • Session storage: PostgreSQL database (not cookies or file-based)
  • Session tracking: UserSession model tracks active sessions per user
  • Session metadata: IP address and user-agent recorded for each session
  • O(1) revocation: Instant invalidation of all user sessions (e.g., on password change)
  • Cookie security: HttpOnly, SameSite=Lax, Secure flags in production
  • Self-service control: users can review their own active sessions (browser, IP address, last activity) and revoke any other session, individually or all at once, from My Profile
  • Idle timeout: sessions expire after 12 hours of inactivity (a sliding window that activity extends); closing the browser ends the session immediately

Local Authentication

For users without SSO, DFIRe provides username/password authentication:

  • Password policy: Minimum 12 characters with complexity requirements
  • Password storage: PBKDF2-SHA256 with per-user salt (Django default)
  • Login rate limiting: 5 attempts per minute per client IP (counted against the real client address, not a spoofable proxy header)
  • Session invalidation: All sessions revoked on password change

OIDC Single Sign-On

DFIRe supports federated authentication via OpenID Connect, allowing integration with enterprise identity providers:

Provider Status
Microsoft Entra ID (Azure AD) Tested
Google Workspace Tested
Okta Supported
Auth0 Tested
Any OIDC-compliant IdP Supported

OIDC Flow

  1. User selects SSO provider on login page
  2. Browser redirects to identity provider's authorization endpoint
  3. User authenticates with the identity provider
  4. IdP redirects back with authorization code
  5. DFIRe exchanges code for tokens via back-channel
  6. Claims extracted from ID token (email, name, phone, picture)
  7. User matched by (provider_id, subject) or email fallback
  8. Session created and user redirected to dashboard

User Provisioning

When a user first authenticates via OIDC:

  • Account is automatically created from OIDC claims
  • Default group assigned based on provider configuration
  • Profile picture downloaded if provided by IdP
  • OIDC identity (provider + subject) stored for future logins

See Single Sign-On for configuration instructions.

Encryption at Rest

DFIRe encrypts all sensitive data at rest using a three-layer key hierarchy. This design ensures that data compromise requires access to multiple keys, and deletion of any key renders associated data permanently unreadable.

Key Hierarchy

Tenant Master Key (32-byte, generated once)
    └── Entity Key (per Case or Evidence Item)
            └── File Key (derived per attachment)
Layer Storage Purpose
Tenant Master Key Database (Tenant.encryption_key) Root key for all tenant data
Entity Key Database (Case/Item.encryption_key) Per-case/item isolation
File Key Derived at runtime Unique per attachment

File Key Derivation

Each attachment has a unique encryption key derived using HKDF-SHA256:

1. intermediate = HKDF(salt=tenant_key, ikm=entity_key, info="dfire-entity-derivation")
2. file_key = HKDF(salt=file_salt, ikm=intermediate, info="dfire-file-encryption")

The file_salt is a random 16-byte value generated for each attachment and stored alongside the encrypted file. This ensures every file has a unique key even within the same case.

File Encryption (Attachments & Photos)

All file attachments and photos are encrypted using AES-256-GCM:

Property Value
Algorithm AES-256-GCM (authenticated encryption)
Chunk size 8MB plaintext per chunk
Nonce 12 bytes, derived from chunk index
Auth tag 16 bytes per chunk
Streaming Yes - supports files of any size

The chunked design enables:

  • Streaming encryption/decryption without buffering entire files in memory
  • Compatibility with S3 multipart uploads
  • Resumable uploads (same key derivation produces same key)
  • Parallel chunk processing

Data Deletion: Deleting a tenant, case, or evidence item permanently destroys the associated encryption key. All attachments encrypted with that key become unreadable with no recovery mechanism. This is by design for secure data disposal.

Credential Encryption

Stored credentials (API keys, webhook secrets, SSO client secrets) are encrypted using a separate key:

Property Value
Algorithm Fernet (AES-128-CBC + HMAC-SHA256)
Key source CREDENTIAL_ENCRYPTION_KEY environment variable
Field types EncryptedCharField, EncryptedJSONField

Encrypted database fields include:

  • OIDC client secrets
  • Slack bot tokens and signing secrets
  • Webhook signing keys
  • License server credentials
  • S3/SMB storage credentials

Key Separation: The CREDENTIAL_ENCRYPTION_KEY is separate from the tenant's file encryption keys. This means database credential encryption is independent of file storage encryption.

Key Requirements: The SECRET_KEY must be at least 32 characters long and must differ from the CREDENTIAL_ENCRYPTION_KEY. DFIRe validates both requirements on startup and will refuse to start if they are not met.

Role-Based Access Control (RBAC)

DFIRe implements a two-tier RBAC model: global permissions control what actions a user can perform, while case-scoped access controls which cases they can access.

Permission Architecture

Can user X perform operation Y on case Z?
│
├─ Is user a superuser?
│  └─ YES → Allow (bypasses all checks)
│
├─ Does user have the atomic permission for Y?
│  └─ NO → Deny
│
├─ Does user have global override (view_all/edit_all)?
│  └─ YES → Allow
│
├─ Is user assigned to case Z?
│  ├─ Lead Investigator → Allow
│  ├─ Investigator → Allow (write operations)
│  └─ Viewer → Allow (read-only operations)
│
├─ Is case Z unassigned (no lead investigator)?
│  └─ User with core.change_case → Allow (the case can be claimed)
│
└─ NO → Deny (no access to this case)

User Types

Type Description
Superuser Bypasses all permission checks and has exclusive access to system-level settings (see Settings Access below). Should be limited to system administrators.
Regular User Permissions come from assigned roles. A user can hold several roles and receives the combined permissions of all of them. Case access is determined by team assignment.

Case Roles

Role Capabilities
Lead Investigator Full control: edit case, manage team, delete case, close case
Investigator Edit access: add/edit evidence, notes, attachments, timeline events
Viewer Read-only access to case and all its contents

Global Override Permissions

Permission Effect
core.view_all_cases View any case regardless of assignment
core.edit_all_cases Edit any case regardless of assignment
core.view_archived_cases View archived cases (required in addition to other access)

Roles and Delegated Administration

Permissions are granted through roles, managed in Settings → Access Roles. A user can hold multiple roles and receives the combined permissions of all of them. Roles can be created, renamed, and deleted in the Role Editor; DFIRe never checks a role's name, only the atomic permissions it grants. See the Role Editor for how to work with roles.

Role and user administration can itself be delegated, with one hard limit: a delegated administrator cannot grant an escalation-sensitive permission (global case access, archived-case access, or user, role, and permission management) unless they hold that permission themselves. The server enforces this on every role change, so a delegated editor cannot escalate their own access.

Settings Access

Each section of System Settings is gated individually, and the Settings page opens for any user who can see at least one section. There is no separate administrator permission for Settings itself.

Superuser-Only Settings

  • Global Settings (tenant configuration, including configuration import/export)
  • Storage
  • Single Sign-On
  • License & Updates
  • Retention Policy
  • Backup & Recovery
  • Scheduled Jobs
  • Dependencies
  • Support
  • Case codenames
  • All integration settings: Slack, Jira, Log Integration, IOC Enrichment, IOC Sharing (TAXII), AI / LLM, and MCP Server

Delegatable by Permission

Reachable by any role that holds the section's own permission, with no other administrative access required. A role can be limited to a single one of these, for example a role that can edit only playbooks:

  • User Accounts
  • Access Roles
  • Webhooks
  • Automation
  • Reporting (report section templates)
  • Compliance Timers
  • Triage Flags
  • Projects
  • Playbooks
  • Runbooks
  • Evidence Types
  • Evidence Workflow
  • Incident Lifecycle
  • Incident Categories
  • Outcome Verdicts

Playbooks, evidence types, triage flags, workflow steps, incident phases, incident categories, and outcome verdicts are readable by every authenticated user; the permissions above gate changes, not visibility. Legal Entities are managed from the Entities page rather than Settings, gated by their own permissions in the same way.

Write Protection

Closed and archived cases are write-protected:

  • Closed: Case is read-only apart from the Investigation Report. Team members with edit access can reopen it.
  • Archived: Case is fully read-only and requires the view_archived_cases permission to view. Reopening restores normal access.

Additional Security Controls

CSRF Protection

Cross-Site Request Forgery protection uses the double-submit cookie pattern:

  • CSRF token set in cookie (csrftoken)
  • Token must be included in X-CSRFToken header for mutations
  • Token validated on all POST, PUT, PATCH, DELETE requests

Rate Limiting

Endpoint Limit
Login 5 requests/minute
API (authenticated) 1500 requests/minute
Slack webhooks 60 requests/minute

Secure Cookies

Flag Value
HttpOnly Yes (session cookie not accessible to JavaScript)
SameSite Lax (prevents CSRF from external sites)
Secure Yes in production (HTTPS only)

WebSocket Authentication

Real-time WebSocket connections:

  • Authenticate using the same session cookie as HTTP requests
  • Re-validate permissions on each ping
  • Scoped to specific resources (case, item, dashboard)
  • Anonymous connections rejected

Audit Logging

All security-sensitive operations are logged to an immutable audit trail:

  • Immutable: Audit records cannot be modified or deleted
  • Fail-closed: Operations fail if audit logging fails
  • Comprehensive: Logs user, action, object, changes, timestamp, IP
  • Searchable: Full-text search across audit records

Logged operations include:

  • Authentication events (login, logout, failed attempts)
  • Case and evidence CRUD operations
  • Team assignment changes
  • Configuration changes
  • Attachment uploads and downloads

Preconfigured Roles

DFIRe comes with four preconfigured roles. These use atomic permissions (not role name checks) and can be customized, or replaced with your own roles, in the Role Editor:

DFIRe Admin

Full administrative access for managing the entire system:

  • core.view_all_cases, core.edit_all_cases - global case access
  • core.view_archived_cases, core.view_statistics
  • auth.add_user, auth.change_user - user management
  • auth.add_group, auth.change_group, auth.delete_group - role management
  • core.*_automationrule - automation rules
  • API key administration (manage_api_keys and user API key management)
  • All case, item, attachment, note permissions
  • IOC management: manage_indicators, export_indicators, import_indicators, manage_taxii and TAXII collection management
  • Webhook (including secrets and delivery history), compliance timer, playbook, evidence type, workflow, incident phase, report template, project, runbook, and legal entity management

Team Lead

Oversight role with full case visibility but no user or system administration:

  • core.view_all_cases, core.edit_all_cases - global case access
  • core.view_archived_cases, core.view_statistics
  • core.manage_team - can manage case assignments
  • All case, item, attachment, note, timeline permissions
  • IOC management: manage_indicators, export_indicators, import_indicators
  • Project, runbook, report template, and legal entity management
  • No user or role administration - System Settings shows only the sections this role's own permissions grant, such as Projects, Runbooks, and Reporting

Standard user

Regular investigator working on assigned cases:

  • core.add_case, core.change_case, core.delete_case
  • core.manage_team - can manage team on cases they lead
  • All item, attachment, note, timeline permissions
  • IOC management: manage_indicators, export_indicators, import_indicators
  • Can create and edit legal entities
  • No global case access - only sees assigned cases

View Only

Read-only access for auditors or external reviewers:

  • core.view_case, core.view_item, core.view_attachment
  • View-only permissions for all case content
  • Cannot create, modify, or delete anything
  • Access limited to assigned cases only