OpenJournal implements multiple layers of security to protect user data and ensure privacy.
Implementation: lib/encryption.ts
All journal entry content is encrypted before being stored in the database using AES-256-GCM encryption.
Features:
- Algorithm: AES-256-GCM (Galois/Counter Mode)
- Key Derivation: PBKDF2 with 100,000 iterations using SHA-256
- Authentication: Built-in authentication tags prevent tampering
- Salt: 64-byte random salt per encrypted value
- IV: 16-byte random initialization vector per encrypted value
Process:
- Plain text content → Encrypted in API layer
- Stored encrypted in database
- Decrypted only when retrieved by authorized user
- Never logged or cached in plain text
Configuration:
- Set
ENCRYPTION_KEYenvironment variable (generate withopenssl rand -base64 32) - Key must be kept secret and backed up securely
- Rotate keys periodically (requires re-encryption migration)
Recommendation: Configure PostgreSQL with transparent data encryption (TDE) or use encrypted volumes.
Options:
-
PostgreSQL Native:
- Enable
pgcryptoextension - Use encrypted tablespaces
- Enable
-
Cloud Provider Solutions:
- AWS RDS: Enable encryption at rest
- Google Cloud SQL: Enable automatic encryption
- Azure Database: Enable transparent data encryption
-
Filesystem Level:
- LUKS (Linux Unified Key Setup)
- dm-crypt for volume encryption
- Cloud provider encrypted volumes
Requirements:
- All connections must use HTTPS
- TLS 1.2 or higher
- Strong cipher suites only
Implementation Options:
# nginx configuration
server {
listen 443 ssl http2;
server_name journal.example.com;
ssl_certificate /path/to/cert.pem;
ssl_certificate_key /path/to/key.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
proxy_pass http://localhost:3000;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
}
}- Vercel: Automatic HTTPS
- Netlify: Automatic HTTPS
- AWS ALB: Configure SSL/TLS certificate
- Google Cloud Load Balancer: Configure SSL certificate
# Using certbot
certbot --nginx -d journal.example.comLocal development uses HTTP by default. For testing HTTPS locally:
# Generate self-signed certificate
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 -nodes
# Update package.json
"dev": "NODE_OPTIONS='--tls-key=key.pem --tls-cert=cert.pem' next dev -p 3001"- Bcrypt hashing with salt rounds: 12
- Minimum password requirements enforced
- Password reset tokens expire after 1 hour
- Secure token generation using cryptographic random
- TOTP (Time-based One-Time Password)
- QR code setup with secret backup
- Recovery codes available
- Enforced for sensitive operations
- HTTP-only cookies
- Secure flag in production
- SameSite: Lax protection
- Session expiration: 30 days (configurable)
- Automatic session cleanup
- State parameter for CSRF protection
- PKCE (Proof Key for Code Exchange) where supported
- Secure token storage
- Token encryption at rest
Authentication Events:
- Login attempts (success/failure)
- Logout events
- Password changes
- 2FA setup/changes
- Account lockouts
Data Access Events:
- Entry creation
- Entry modifications
- Entry deletions
- Entry views (optional, configurable)
Security Events:
- Failed authentication attempts
- Permission denied errors
- Suspicious activity patterns
- API rate limit violations
interface AuditLog {
id: string;
userId: string;
action: string; // e.g., "entry.create", "auth.login"
resourceType: string; // e.g., "entry", "user"
resourceId: string | null;
ipAddress: string;
userAgent: string;
metadata: object; // Additional context
timestamp: Date;
success: boolean;
}- Security logs: 90 days minimum
- Compliance logs: As required by regulations
- Access logs: 30 days
- Auto-archival to cold storage for long-term retention
- Only collect necessary data
- No tracking cookies without consent
- Minimal metadata collection
- User-configurable data retention
- Export all personal data (GDPR compliance)
- Delete account and all associated data
- View audit logs of their own data access
- Control data sharing preferences
- Role-based access control (RBAC)
- User can only access their own data
- Admin access logged and audited
- Principle of least privilege
// middleware.ts
export function middleware(request: NextRequest) {
const response = NextResponse.next();
// Security headers
response.headers.set('X-Frame-Options', 'DENY');
response.headers.set('X-Content-Type-Options', 'nosniff');
response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');
response.headers.set('Permissions-Policy', 'geolocation=(), microphone=(), camera=()');
response.headers.set(
'Content-Security-Policy',
"default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';"
);
return response;
}- Rate limiting per IP address
- Rate limiting per user account
- Exponential backoff on failures
- CAPTCHA for repeated failures
- Authentication: 5 attempts per 15 minutes
- API endpoints: 100 requests per minute
- Entry creation: 10 per minute
- Export operations: 1 per hour
- Dependencies updated monthly
- Security patches applied immediately
- Automated dependency scanning (Dependabot)
- Regular security audits
- Security issues: security@openjournal.example
- Bug bounty program (if applicable)
- Responsible disclosure policy
- 90-day disclosure timeline
- GDPR (General Data Protection Regulation)
- CCPA (California Consumer Privacy Act)
- SOC 2 Type II (if applicable)
- HIPAA compliance considerations (for medical journaling)
- Regular penetration testing
- Third-party security audits
- Compliance certifications maintained
- Detect and identify security incident
- Contain and mitigate immediate threat
- Investigate root cause
- Remediate vulnerabilities
- Notify affected users (if required)
- Document and learn from incident
- Security team: security@openjournal.example
- Emergency hotline: +1-XXX-XXX-XXXX
- Public disclosure: security page on website
# Encryption
ENCRYPTION_KEY= # AES-256 encryption key
# Authentication
NEXTAUTH_SECRET= # NextAuth.js secret
NEXTAUTH_URL= # Application URL
# Database (use SSL in production)
DATABASE_URL= # PostgreSQL connection with SSL
# OAuth (store securely)
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=Development:
.env.local(git-ignored)- Never commit secrets to version control
Production:
- Environment variable injection
- Secret management services (AWS Secrets Manager, HashiCorp Vault)
- Encrypted secret storage
- Access audit logging
- Use strong, unique passwords
- Enable two-factor authentication
- Review audit logs regularly
- Report suspicious activity
- Keep recovery codes secure
- Never log sensitive data
- Use parameterized queries (prevent SQL injection)
- Validate all user input
- Follow principle of least privilege
- Keep dependencies updated
- Use security linters and scanners
- Conduct code reviews for security
- Enable all security features
- Monitor audit logs
- Set up alerting for security events
- Regular security assessments
- Maintain backup and recovery procedures
- Document security procedures
- Train team on security practices
- All environment variables set securely
- HTTPS/TLS enabled and configured
- Database encryption at rest enabled
- Security headers configured
- Rate limiting implemented
- Audit logging enabled
- Session security configured
- Password policies enforced
- 2FA available and encouraged
- CSRF protection enabled
- XSS protection in place
- SQL injection prevention verified
- Dependency vulnerabilities checked
- Security testing completed
- Incident response plan documented
- Backup and recovery tested