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
- User selects SSO provider on login page
- Browser redirects to identity provider's authorization endpoint
- User authenticates with the identity provider
- IdP redirects back with authorization code
- DFIRe exchanges code for tokens via back-channel
- Claims extracted from ID token (email, name, phone, picture)
- User matched by (provider_id, subject) or email fallback
- 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_casespermission 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-CSRFTokenheader 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 accesscore.view_archived_cases,core.view_statisticsauth.add_user,auth.change_user- user managementauth.add_group,auth.change_group,auth.delete_group- role managementcore.*_automationrule- automation rules- API key administration (
manage_api_keysand user API key management) - All case, item, attachment, note permissions
- IOC management:
manage_indicators,export_indicators,import_indicators,manage_taxiiand 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 accesscore.view_archived_cases,core.view_statisticscore.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_casecore.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