18 KiB
Vendored
Security Architecture
This document provides a comprehensive overview of Trilium's security architecture, including threat models, security boundaries, and implementation details for developers.
Security Model Overview
Design Principles
Trilium's security architecture is built on several key principles:
- Defense in Depth: Multiple layers of security controls
- Principle of Least Privilege: Minimal necessary access rights
- Secure by Default: Secure configuration out of the box
- Encryption First: Sensitive data encrypted at rest
- Zero Trust: Verify all requests and access attempts
Threat Model
Assets to Protect
Primary Assets:
- User notes and content
- Encrypted data keys
- Authentication credentials
- Session information
- Configuration data
Secondary Assets:
- System availability
- Data integrity
- User privacy
- Application functionality
Threat Actors
External Attackers:
- Network-based attackers
- Malicious web traffic
- Data thieves
- Denial of service attackers
Internal Threats:
- Compromised user accounts
- Malicious scripts
- Insider threats
- Accidental disclosure
Attack Vectors
Network Attacks:
- Man-in-the-middle attacks
- Session hijacking
- Cross-site scripting
- SQL injection attempts
Application Attacks:
- Authentication bypass
- Authorization flaws
- Input validation failures
- Business logic exploits
Data Attacks:
- Database theft
- Backup compromise
- Memory dumps
- File system access
Security Boundaries
Client-Server Boundary
The boundary between client (browser/desktop) and server represents a critical security boundary.
Trust Model
┌─────────────────┐ HTTPS/WSS ┌─────────────────┐
│ Client Side │ ◄────────────── │ Server Side │
│ (Untrusted) │ │ (Trusted) │
│ │ │ │
│ • User Input │ │ • Authentication │
│ • Display Logic │ │ • Authorization │
│ • Client State │ │ • Data Storage │
│ • Local Cache │ │ • Encryption │
└─────────────────┘ └─────────────────┘
Security Controls
Client-Side Controls:
- Input validation (first line of defense)
- XSS prevention
- CSRF token handling
- Secure storage practices
Server-Side Controls:
- Authentication verification
- Authorization enforcement
- Input sanitization
- Data encryption
- Audit logging
Protected Session Boundary
Protected sessions create a security boundary around encrypted content.
Session Lifecycle
┌─────────────────┐
│ Public Session │ ─────┐
│ │ │ Enter Password
│ • Unprotected │ │
│ content only │ ▼
│ │ ┌─────────────────┐
└─────────────────┘ │ Protected Session│
│ │
│ • Encrypted │ ◄── Timeout
│ content │
│ • Data key │
│ in memory │
└─────────────────┘
Security Properties
Inside Protected Session:
- Data key available in memory
- Encrypted content accessible
- All protected operations allowed
- Session timeout monitoring active
Outside Protected Session:
- No data key in memory
- Encrypted content inaccessible
- Protected operations blocked
- Enhanced security posture
Process Boundary
In multi-process deployments, process boundaries provide additional security.
Process Isolation
┌─────────────────┐ ┌─────────────────┐
│ Web Process │ │ Worker Process │
│ │ │ │
│ • HTTP handling │ │ • Background │
│ • Session mgmt │ ◄──┤ tasks │
│ • Authentication │ │ • File processing │
│ │ │ • Maintenance │
└─────────────────┘ └─────────────────┘
Encryption Architecture
Key Management Hierarchy
Master Password
│
▼
Password-Derived Key ◄── Scrypt + Salt
│
▼
Data Key (encrypted) ──┐
│ │
▼ │
Protected Content ◄─────────┘
(encrypted)
Key Derivation Process
- Master Password: User-provided secret
- Salt Generation: Cryptographically secure random salt
- Key Derivation: Scrypt(password, salt, N=16384, r=8, p=1)
- Data Key Encryption: AES-128-CBC(password_key, data_key)
- Content Encryption: AES-128-CBC(data_key, content)
Encryption Implementation
Data Encryption Service
// Core encryption interface
interface DataEncryptionService {
encrypt(key: Buffer, plaintext: Buffer | string): string;
decrypt(key: Buffer, ciphertext: string): Buffer | false | null;
decryptString(key: Buffer, ciphertext: string): string | null;
}
Encryption Process Flow
Plaintext → SHA-1 Digest → Prepend → AES-128-CBC → Base64 → Ciphertext
(4 bytes) to data Encrypt Encode
↓
Random IV (16 bytes)
Decryption Process Flow
Ciphertext → Base64 → Extract IV → AES-128-CBC → Extract → Verify → Plaintext
Decode & Encrypted Decrypt Digest Digest
Data & Data
Authentication Architecture
Multi-Factor Authentication Flow
┌─────────────────┐
│ Password Auth │
└─────────┬───────┘
│
▼
┌─────────────────┐ ┌─────────────────┐
│ Primary Factor │ │ Secondary Factor │
│ Verified │ ──▶│ Required │
│ │ │ │
│ • Password │ │ • TOTP Code │
│ • Hash Check │ │ • Recovery Code │
└─────────────────┘ └─────────┬───────┘
│
▼
┌─────────────────┐
│ Session Created │
│ │
│ • Authenticated │
│ • State Tracked │
└─────────────────┘
TOTP Implementation
Secret Generation and Storage
interface TOTPSecurity {
// Secret generation
generateSecret(): string; // Base32-encoded secret
// Encrypted storage
setTotpSecret(secret: string): void; // Encrypt and store
getTotpSecret(): string | null; // Decrypt and return
// Verification
verifyTotpSecret(secret: string): boolean; // Hash comparison
validateTOTP(code: string): boolean; // Time-based validation
}
Recovery Code System
interface RecoveryCodeSystem {
// Code generation
generateRecoveryCodes(): string[]; // Base64 codes
// Encrypted storage
setRecoveryCodes(codes: string[]): void; // AES-256-CBC
getRecoveryCodes(): string[]; // Decrypt and return
// Usage tracking
verifyRecoveryCode(code: string): boolean; // One-time use
markCodeUsed(code: string): void; // Replace with timestamp
}
Session Management Architecture
Session Storage
Database Schema
CREATE TABLE sessions (
id TEXT PRIMARY KEY, -- Session identifier
expires INTEGER NOT NULL, -- Expiration timestamp
data TEXT NOT NULL -- JSON session data
);
CREATE INDEX idx_sessions_expires ON sessions(expires);
Session Data Structure
interface SessionData {
loggedIn: boolean; // Authentication status
lastAuthState: { // Previous auth configuration
totpEnabled: boolean;
ssoEnabled: boolean;
};
protectedSession?: { // Protected session state
active: boolean;
lastActivity: number;
};
csrf?: string; // CSRF token
}
Session Security Controls
Session Creation
- Secure ID Generation: Cryptographically secure random session IDs
- Expiration Setting: Configurable session timeouts
- State Initialization: Secure default session state
- Database Storage: Atomic session creation
Session Validation
- ID Verification: Validate session ID format and existence
- Expiration Check: Verify session hasn't expired
- State Validation: Check authentication and authorization state
- Security Context: Validate security configuration consistency
Session Cleanup
- Automatic Expiration: Remove expired sessions periodically
- Manual Cleanup: Explicit session termination
- Security Events: Force cleanup on security state changes
- Database Maintenance: Optimize session storage
CSRF Protection Architecture
Double Submit Cookie Pattern
┌─────────────────┐ Token in Cookie ┌─────────────────┐
│ Client │ ◄─────────────────────│ Server │
│ │ │ │
│ • Store token │ Token in Header │ • Generate │
│ • Send header │ ─────────────────────▶│ • Validate │
│ │ │ • Compare │
└─────────────────┘ └─────────────────┘
Token Lifecycle
- Generation: Create cryptographically secure token
- Distribution: Send token in secure cookie
- Validation: Require token in request headers
- Comparison: Verify cookie and header tokens match
- Rotation: Regenerate tokens periodically
Authorization Architecture
Access Control Model
Trilium uses a simplified access control model based on authentication state and note properties.
Permission Matrix
Operation │ Public │ Authenticated │ Protected Session
─────────────────────┼────────┼───────────────┼──────────────────
Read Public Notes │ ✓ │ ✓ │ ✓
Create Notes │ ✗ │ ✓ │ ✓
Modify Notes │ ✗ │ ✓ │ ✓
Read Protected │ ✗ │ ✗ │ ✓
Modify Protected │ ✗ │ ✗ │ ✓
Admin Operations │ ✗ │ ✓ │ ✓
API Security
Endpoint Protection
// Authentication middleware chain
const protectedRoute = [
checkAuth, // Verify authentication
checkApiAuth, // API-specific checks
doubleCsrfProtection, // CSRF protection
routeHandler // Business logic
];
// ETAPI token authentication
const etapiRoute = [
checkEtapiToken, // Token validation
rateLimiter, // Rate limiting
routeHandler // Business logic
];
Input Validation
interface InputValidation {
// Type validation
validateType(input: any, expectedType: string): boolean;
// Size limits
validateSize(input: string, maxLength: number): boolean;
// Content sanitization
sanitizeHTML(input: string): string;
// SQL injection prevention
parameterizeQuery(query: string, params: any[]): string;
}
Security Event Architecture
Event Types
Authentication Events
enum AuthenticationEvents {
LOGIN_SUCCESS = 'auth.login.success',
LOGIN_FAILURE = 'auth.login.failure',
LOGOUT = 'auth.logout',
MFA_CHALLENGE = 'auth.mfa.challenge',
MFA_SUCCESS = 'auth.mfa.success',
MFA_FAILURE = 'auth.mfa.failure',
PASSWORD_CHANGE = 'auth.password.change'
}
Session Events
enum SessionEvents {
SESSION_CREATE = 'session.create',
SESSION_DESTROY = 'session.destroy',
SESSION_EXPIRE = 'session.expire',
PROTECTED_ENTER = 'session.protected.enter',
PROTECTED_EXIT = 'session.protected.exit',
PROTECTED_TIMEOUT = 'session.protected.timeout'
}
Security Events
enum SecurityEvents {
CSRF_VIOLATION = 'security.csrf.violation',
RATE_LIMIT_EXCEEDED = 'security.rate.exceeded',
INVALID_TOKEN = 'security.token.invalid',
SUSPICIOUS_ACTIVITY = 'security.suspicious',
ENCRYPTION_FAILURE = 'security.encryption.failure'
}
Event Processing
Event Handler Architecture
interface SecurityEventHandler {
// Event processing
handleEvent(event: SecurityEvent): void;
// Alert generation
generateAlert(severity: AlertSeverity, event: SecurityEvent): void;
// Response actions
executeResponse(event: SecurityEvent, action: ResponseAction): void;
}
Audit Logging
interface AuditLogger {
// Standard logging
logEvent(event: SecurityEvent): void;
// Structured logging
logStructured(level: LogLevel, data: AuditData): void;
// Security-specific logging
logSecurityEvent(event: SecurityEvent, context: SecurityContext): void;
}
Threat Detection and Response
Intrusion Detection
Behavioral Analysis
- Login Pattern Analysis: Detect unusual login patterns
- Session Anomalies: Identify suspicious session behavior
- Access Patterns: Monitor unusual data access
- Rate Limiting: Detect and prevent abuse
Threat Indicators
interface ThreatIndicators {
// Authentication anomalies
multipleFailedLogins(timeWindow: number, threshold: number): boolean;
// Session anomalies
sessionHijackingPattern(sessionId: string): boolean;
// Data access anomalies
unusualDataAccess(userId: string, pattern: AccessPattern): boolean;
// Network anomalies
suspiciousNetworkActivity(ip: string, pattern: NetworkPattern): boolean;
}
Incident Response
Automated Response
- Account Lockout: Temporary account suspension
- Session Termination: Force logout of suspicious sessions
- Rate Limiting: Dynamic rate limit adjustment
- Alert Generation: Notify administrators of threats
Response Actions
enum ResponseActions {
LOG_ONLY = 'log', // Record event only
ALERT = 'alert', // Generate alert
BLOCK_IP = 'block_ip', // Block source IP
LOCK_ACCOUNT = 'lock_account', // Suspend account
FORCE_LOGOUT = 'force_logout', // Terminate sessions
REQUIRE_MFA = 'require_mfa' // Force MFA challenge
}
Security Testing
Security Test Categories
Unit Tests
- Encryption Tests: Verify encryption/decryption operations
- Authentication Tests: Test password verification and MFA
- Session Tests: Validate session management
- Input Validation Tests: Test sanitization functions
Integration Tests
- Authentication Flow: End-to-end authentication testing
- Session Management: Multi-client session testing
- API Security: Test API authentication and authorization
- CSRF Protection: Validate CSRF token handling
Security Tests
- Penetration Testing: Simulated attack scenarios
- Vulnerability Scanning: Automated security scanning
- Fuzzing: Input validation stress testing
- Security Code Review: Manual code analysis
Test Implementation Examples
Encryption Tests
describe('Data Encryption Service', () => {
test('should encrypt and decrypt data correctly', () => {
const key = crypto.randomBytes(16);
const plaintext = 'sensitive data';
const encrypted = dataEncryption.encrypt(key, plaintext);
const decrypted = dataEncryption.decryptString(key, encrypted);
expect(decrypted).toBe(plaintext);
});
test('should fail with wrong key', () => {
const key1 = crypto.randomBytes(16);
const key2 = crypto.randomBytes(16);
const plaintext = 'sensitive data';
const encrypted = dataEncryption.encrypt(key1, plaintext);
const decrypted = dataEncryption.decrypt(key2, encrypted);
expect(decrypted).toBe(false);
});
});
Authentication Tests
describe('TOTP Authentication', () => {
test('should validate correct TOTP code', () => {
const secret = totpService.createSecret();
const code = totp.generate(secret.message);
const isValid = totpService.validateTOTP(code);
expect(isValid).toBe(true);
});
test('should reject expired TOTP code', () => {
const secret = totpService.createSecret();
const expiredCode = '000000'; // Known invalid code
const isValid = totpService.validateTOTP(expiredCode);
expect(isValid).toBe(false);
});
});
This security architecture provides a comprehensive foundation for understanding and maintaining Trilium's security posture. Regular review and updates ensure the architecture remains effective against evolving threats.