Update Apache SSL configuration and enhance security features across multiple files. Changed X-Frame-Options to SAMEORIGIN for better security, added optional Content Security Policy headers for testing, and improved password handling with HaveIBeenPwned checks during user registration and password reset. Implemented passkey login functionality in the authentication flow, including UI updates for user experience. Enhanced image upload processing with size limits and validation, and added rate limiting for various API endpoints to prevent abuse.
Some checks failed
Code Analysis (JS/Vue) / analyze (push) Failing after 51s

This commit is contained in:
Torsten Schulz (local)
2026-01-05 11:50:57 +01:00
parent 8bd7ed76cd
commit 673c34ac9d
47 changed files with 1738 additions and 83 deletions

View File

@@ -1,9 +1,16 @@
import crypto from 'crypto'
// Verschlüsselungskonfiguration
const ALGORITHM = 'aes-256-cbc'
const IV_LENGTH = 16
// v1 (legacy): aes-256-cbc (ohne Authentizitätsschutz)
const LEGACY_ALGORITHM = 'aes-256-cbc'
const LEGACY_IV_LENGTH = 16
// v2 (default): aes-256-gcm (AEAD, state-of-the-art)
const ALGORITHM = 'aes-256-gcm'
const IV_LENGTH = 12
const AUTH_TAG_LENGTH = 16
const SALT_LENGTH = 32
const VERSION_PREFIX = 'v2:'
/**
* Generiert einen Schlüssel aus einem Passwort und Salt
@@ -12,35 +19,87 @@ function deriveKey(password, salt) {
return crypto.pbkdf2Sync(password, salt, 100000, 32, 'sha512')
}
function encryptV2GCM(text, password) {
// Salt generieren
const salt = crypto.randomBytes(SALT_LENGTH)
// Schlüssel ableiten
const key = deriveKey(password, salt)
// IV generieren (12 bytes ist Best Practice für GCM)
const iv = crypto.randomBytes(IV_LENGTH)
// Cipher erstellen
const cipher = crypto.createCipheriv(ALGORITHM, key, iv)
// Verschlüsseln
const encrypted = Buffer.concat([
cipher.update(text, 'utf8'),
cipher.final()
])
// Auth-Tag holen
const tag = cipher.getAuthTag()
// Salt + IV + Tag + Ciphertext kombinieren
const combined = Buffer.concat([salt, iv, tag, encrypted])
return `${VERSION_PREFIX}${combined.toString('base64')}`
}
function decryptLegacyCBC(encryptedData, password) {
// Base64 dekodieren
const combined = Buffer.from(encryptedData, 'base64')
// Komponenten extrahieren (v1: salt(32) + iv(16) + ciphertext)
const salt = combined.subarray(0, SALT_LENGTH)
const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + LEGACY_IV_LENGTH)
const encrypted = combined.subarray(SALT_LENGTH + LEGACY_IV_LENGTH)
// Schlüssel ableiten
const key = deriveKey(password, salt)
// Decipher erstellen
const decipher = crypto.createDecipheriv(LEGACY_ALGORITHM, key, iv)
// Entschlüsseln
const decrypted = Buffer.concat([
decipher.update(encrypted),
decipher.final()
])
return decrypted.toString('utf8')
}
function decryptV2GCM(encryptedData, password) {
const b64 = encryptedData.slice(VERSION_PREFIX.length)
const combined = Buffer.from(b64, 'base64')
// v2: salt(32) + iv(12) + tag(16) + ciphertext
const salt = combined.subarray(0, SALT_LENGTH)
const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH)
const tagStart = SALT_LENGTH + IV_LENGTH
const tag = combined.subarray(tagStart, tagStart + AUTH_TAG_LENGTH)
const encrypted = combined.subarray(tagStart + AUTH_TAG_LENGTH)
const key = deriveKey(password, salt)
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv)
decipher.setAuthTag(tag)
const decrypted = Buffer.concat([
decipher.update(encrypted),
decipher.final()
])
return decrypted.toString('utf8')
}
/**
* Verschlüsselt einen Text
*/
export function encrypt(text, password) {
try {
// Salt generieren
const salt = crypto.randomBytes(SALT_LENGTH)
// Schlüssel ableiten
const key = deriveKey(password, salt)
// IV generieren
const iv = crypto.randomBytes(IV_LENGTH)
// Cipher erstellen
const cipher = crypto.createCipheriv(ALGORITHM, key, iv)
// Verschlüsseln
let encrypted = cipher.update(text, 'utf8', 'hex')
encrypted += cipher.final('hex')
// Salt + IV + Verschlüsselter Text kombinieren
const combined = Buffer.concat([
salt,
iv,
Buffer.from(encrypted, 'hex')
])
return combined.toString('base64')
return encryptV2GCM(text, password)
} catch (error) {
console.error('Verschlüsselungsfehler:', error)
throw new Error('Fehler beim Verschlüsseln der Daten')
@@ -52,25 +111,12 @@ export function encrypt(text, password) {
*/
export function decrypt(encryptedData, password) {
try {
// Base64 dekodieren
const combined = Buffer.from(encryptedData, 'base64')
// Komponenten extrahieren
const salt = combined.subarray(0, SALT_LENGTH)
const iv = combined.subarray(SALT_LENGTH, SALT_LENGTH + IV_LENGTH)
const encrypted = combined.subarray(SALT_LENGTH + IV_LENGTH)
// Schlüssel ableiten
const key = deriveKey(password, salt)
// Decipher erstellen
const decipher = crypto.createDecipheriv(ALGORITHM, key, iv)
// Entschlüsseln
let decrypted = decipher.update(encrypted, null, 'utf8')
decrypted += decipher.final('utf8')
return decrypted
if (typeof encryptedData === 'string' && encryptedData.startsWith(VERSION_PREFIX)) {
return decryptV2GCM(encryptedData, password)
}
// Fallback: legacy CBC ohne Prefix
return decryptLegacyCBC(encryptedData, password)
} catch (error) {
console.error('Entschlüsselungsfehler:', error)
throw new Error('Fehler beim Entschlüsseln der Daten')