Security Architecture
Transparent security documentation for this portfolio
Security-First Approach
I built this site with the same security standards I apply to every project. This page documents what I've implemented and why — not to impress, but because I believe security decisions should be visible and reviewable.
Philosophy: Transparency over obscurity. Every measure documented here stands on its own merits.
Live CI Status
Source available on requestAdvisories & Licenses
Vulnerability Scan
Lint Checks
Test Suite
Content-Security-Policy (CSP)
default-src 'self'; script-src 'self'; style-src 'self'; img-src 'self' data: https:;
font-src 'self'; connect-src 'self'; frame-ancestors 'none'; base-uri 'self'; form-action
'self'; upgrade-insecure-requests; report-uri /api/csp-report; report-to csp-endpoint
Purpose: Controls which resources the browser is allowed to load
Prevents: Cross-Site Scripting (XSS), Data Injection, Clickjacking
X-Frame-Options
DENY
Purpose: Prevents the page from being embedded in frames/iframes
Prevents: Clickjacking attacks
X-Content-Type-Options
nosniff
Purpose: Prevents browsers from MIME-sniffing responses
Prevents: MIME confusion attacks, malicious file execution
Strict-Transport-Security (HSTS)
max-age=31536000; includeSubDomains; preload
Purpose: Forces all connections to use HTTPS for 1 year
Prevents: Man-in-the-Middle (MitM) attacks, SSL stripping
Additional Headers
- Referrer-Policy: Controls referrer information leakage
- Permissions-Policy: Disables 8 browser features (geolocation, camera, microphone, payment, USB, magnetometer, gyroscope, accelerometer)
- X-XSS-Protection: Legacy auditor disabled (CSP provides XSS protection)
- Cross-Origin-Opener-Policy: Isolates browsing context (Spectre protection)
- Cross-Origin-Resource-Policy: Prevents cross-origin resource embedding
- X-Permitted-Cross-Domain-Policies: Blocks Flash/Acrobat policy loading
- X-Request-ID: Unique UUID for audit trails
General Endpoints
Limit: 100 requests per 60 seconds per IP
Applies to: All public endpoints
Authentication Endpoints
Limit: 5 requests per 60 seconds per IP
Purpose: Prevents brute force attacks
Implementation: actix-governor with RealIpKeyExtractor for accurate client identification
Password Security
- Algorithm: Argon2id (OWASP recommended)
- Why Argon2id: Memory-hard, resistant to GPU/ASIC attacks
- Policy: NIST 800-63B (length-based, common password blocklist)
Session Management
- Access Tokens: JWT with 15-minute expiry
- Refresh Tokens: 7-day expiry with rotation
- Storage: JSON response body, Authorization header
- CSRF: Immune (Authorization header, not cookies)
- Token Blacklist: Redis-backed JWT revocation on logout
Conducted February 2026 covering 8 phases: reconnaissance, TLS/SSL analysis, web application scanning, authentication testing, injection testing, configuration review, infrastructure scanning, and exploitation attempts.
Full report available on request via contact form.
- Active PostgreSQL 17: Strong typing and ACID compliance
- Active Connection Pooling: Prevents connection exhaustion
- Active Prepared Statements: SQL injection prevention via SQLx
- Defined Row-Level Security: Policies defined in migrations, not yet enforced at application level
Security Policy
- Dependabot: Automated dependency update alerts via GitHub
- Security Patches: Applied immediately upon discovery
- CI Integration: cargo audit + cargo deny + clippy on every build
- Minimal Dependencies: Only essential, well-maintained crates
Server Infrastructure (Active)
- Host: Hetzner Cloud Helsinki (Debian 13)
- Service: systemd unit with automatic restart
- Proxy: nginx reverse proxy with TLS termination
- Firewall: UFW — ports 22, 80, 443 only
Operations (Active)
- HTTPS: Let's Encrypt with auto-renewal
- Backups: Daily PostgreSQL backups with rotation
- Monitoring: Health checks every 5 minutes (ntfy.sh alerts)
- Access: Dedicated deploy user with locked permissions
- Active Input Sanitisation: Submissions sanitised before database storage (stored-XSS prevention)
- Active PGP-Encrypted Notifications: Messages encrypted to admin's PGP public key before email delivery — content never transmitted in plaintext
- Active Fail-Open Design: Email or notification failures never block the visitor's form submission
Security Contact & Responsible Disclosure
Responsible disclosure is appreciated. Security issues will be addressed within 48 hours.
Initial response within 24 hours, patches within 48–72 hours depending on severity.