security: 9 Findings aus Security-Audit behoben (CRITICAL + HIGH + MEDIUM)
CRITICAL: - C-1: LDAP tls_verify Default False → True (MITM-Schutz) - C-2: TOTP-Secret Fernet-verschlüsselt in DB (statt Plaintext) - core/crypto.py: encrypt_value() / decrypt_value() helper - Migration 0026: totp_secret VARCHAR(64→500), ldap tls_verify default=true - _totp_plain() helper mit Legacy-Fallback für bestehende Werte HIGH: - H-1: Kiosk Nonce-Cache asyncio.Lock (Race Condition behoben) - H-2: File-Upload-Limit 10 MB (import_kimai.py + users.py CSV-Import) - H-3: CORS allow_methods/allow_headers explizit eingeschränkt (war *) - H-4: TrustedHostMiddleware aktiviert wenn ALLOWED_HOSTS gesetzt MEDIUM: - M-1: IP-Logging nutzt X-Forwarded-For hinter nginx-Proxy - M-4: Audit-Log für password_changed, totp_enabled, totp_disabled - M-5: CalDAV verify_ssl in Production erzwungen (_effective_verify_ssl) 152/152 Tests grün Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
from typing import Annotated
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, File, Query, UploadFile
|
||||
from fastapi import APIRouter, Depends, File, HTTPException, Query, UploadFile
|
||||
from fastapi.responses import PlainTextResponse
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
@@ -90,13 +90,27 @@ async def import_template(
|
||||
)
|
||||
|
||||
|
||||
_MAX_UPLOAD_BYTES = 10 * 1024 * 1024 # 10 MB
|
||||
|
||||
|
||||
async def _read_upload(file: UploadFile) -> bytes:
|
||||
"""Liest eine UploadFile mit Größenbegrenzung (max 10 MB)."""
|
||||
content = await file.read(_MAX_UPLOAD_BYTES + 1)
|
||||
if len(content) > _MAX_UPLOAD_BYTES:
|
||||
raise HTTPException(
|
||||
status_code=413,
|
||||
detail=f"Datei zu groß. Maximale Upload-Größe: {_MAX_UPLOAD_BYTES // (1024 * 1024)} MB.",
|
||||
)
|
||||
return content
|
||||
|
||||
|
||||
@router.post("/import/preview", response_model=UserImportResult)
|
||||
async def user_import_preview(
|
||||
file: Annotated[UploadFile, File()],
|
||||
current_user: User = require_role(*_admin_roles),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
content = await file.read()
|
||||
content = await _read_upload(file)
|
||||
result = await user_import_service.preview_csv(content, current_user.company_id, current_user, db)
|
||||
return _to_import_result_schema(result)
|
||||
|
||||
@@ -107,7 +121,7 @@ async def user_import_apply(
|
||||
current_user: User = require_role(*_admin_roles),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
content = await file.read()
|
||||
content = await _read_upload(file)
|
||||
result = await user_import_service.apply_csv(content, current_user.company_id, current_user, db)
|
||||
return _to_import_result_schema(result)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user