implementet encryption and decryption to the audit to provide a complete lak of context for a petential attacker

This commit is contained in:
2026-06-27 13:45:21 +02:00
parent 7e258e07ab
commit 7bedfc1558
2 changed files with 58 additions and 52 deletions
+22 -39
View File
@@ -388,6 +388,8 @@ ALLOWED_COVER_DOMAINS = {
"www.googleapis.com" "www.googleapis.com"
} }
SENSITIVE_AUDIT_FIELDS = ["email", "username", "full_name", "phone"]
# Apply the configuration for general use throughout the app # Apply the configuration for general use throughout the app
APP_VERSION = __version__ APP_VERSION = __version__
RELEASE_STATE_FILE = os.path.join(os.path.dirname(BASE_DIR), '.release-version') RELEASE_STATE_FILE = os.path.join(os.path.dirname(BASE_DIR), '.release-version')
@@ -726,9 +728,9 @@ def _append_audit_event_standalone(event_type, payload):
al.append_audit_event( al.append_audit_event(
db=db, db=db,
event_type=event_type, event_type=event_type,
actor=encrypt_text(session.get('username', 'system')), actor=session.get('username', 'system'),
payload=encrypt_text(str(payload)), payload=str(payload),
request_ip=encrypt_text(request.remote_addr), request_ip=request.remote_addr,
source='web', source='web',
) )
except Exception as exc: except Exception as exc:
@@ -8044,23 +8046,14 @@ def admin_audit_dashboard():
al.ensure_audit_indexes(db) al.ensure_audit_indexes(db)
verify_result = al.verify_audit_chain(db) verify_result = al.verify_audit_chain(db)
audit_rows = list( audit_rows = list(db['audit_log'].find(...).sort('chain_index', -1).limit(200))
db['audit_log'].find(
{}, # DEC_START: Decrypt the sensitive fields for display
{ for row in audit_rows:
'chain_index': 1, if "payload" in row:
'event_type': 1, # decrypt_document_fields acts in-place
'actor': 1, decrypt_document_fields(row["payload"], SENSITIVE_AUDIT_FIELDS)
'source': 1, # DEC_END
'ip': 1,
'timestamp': 1,
'created_at': 1,
'entry_hash': 1,
'prev_hash': 1,
'payload': 1,
}
).sort('chain_index', -1).limit(200)
)
return render_template( return render_template(
'admin_audit.html', 'admin_audit.html',
@@ -8105,28 +8098,18 @@ def admin_audit_export_pdf_official():
]) ])
) )
audit_rows = list( audit_rows = list(db['audit_log'].find(...).sort('chain_index', -1).limit(limit))
db['audit_log'].find(
{}, # DEC_START: Decrypt sensitive fields for the PDF report
{ for row in audit_rows:
'chain_index': 1, if "payload" in row:
'event_type': 1, decrypt_document_fields(row["payload"], SENSITIVE_AUDIT_FIELDS)
'actor': 1, # DEC_END
'source': 1,
'ip': 1,
'timestamp': 1,
'created_at': 1,
'entry_hash': 1,
'prev_hash': 1,
'payload': 1,
}
).sort('chain_index', -1).limit(limit)
)
# Get school information from settings or use defaults # Get school information from settings or use defaults
school_info = _get_school_info_for_export() school_info = _get_school_info_for_export()
# Generate PDF # Generate PDF with now-decrypted audit_rows
pdf_content = pdf_export.generate_audit_pdf( pdf_content = pdf_export.generate_audit_pdf(
verify_result=verify_result, verify_result=verify_result,
event_counts=event_counts, event_counts=event_counts,
@@ -8134,7 +8117,7 @@ def admin_audit_export_pdf_official():
export_type='official', export_type='official',
school_info=school_info school_info=school_info
) )
response = make_response(pdf_content) response = make_response(pdf_content)
response.headers['Content-Type'] = 'application/pdf' response.headers['Content-Type'] = 'application/pdf'
response.headers['Content-Disposition'] = f'attachment; filename=audit-official-report-{datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%S")}.pdf' response.headers['Content-Disposition'] = f'attachment; filename=audit-official-report-{datetime.datetime.utcnow().strftime("%Y%m%d-%H%M%S")}.pdf'
+36 -13
View File
@@ -10,6 +10,7 @@ import hashlib
import json import json
import random import random
import time import time
from Web.modules.inventarsystem.data_protection import encrypt_document_fields, decrypt_document_fields
from pymongo.errors import DuplicateKeyError from pymongo.errors import DuplicateKeyError
@@ -24,21 +25,34 @@ def _entry_hash(prev_hash, payload):
base = f"{prev_hash}|{_stable_json(payload)}" base = f"{prev_hash}|{_stable_json(payload)}"
return hashlib.sha256(base.encode("utf-8")).hexdigest() return hashlib.sha256(base.encode("utf-8")).hexdigest()
def get_decrypted_audit_logs(db, query=None, decrypt_fields=None):
def append_audit_event(db, event_type, actor, payload, request_ip=None, source="web", max_retries=5):
""" """
Append an audit event to a tamper-evident chain. Retrieve and decrypt audit logs for analysis.
Args: Args:
db: MongoDB database handle. query (dict): MongoDB query filter.
event_type (str): Event category. decrypt_fields (list): Fields within the 'payload' that should be decrypted.
actor (str): User/system who performed the action. """
payload (dict): Event details. logs = db["audit_log"]
request_ip (str, optional): Request origin. cursor = logs.find(query or {}).sort("chain_index", 1)
source (str): Source subsystem.
results = []
for entry in cursor:
# Decrypt specific fields if provided
if decrypt_fields:
decrypt_document_fields(entry.get("payload", {}), decrypt_fields)
results.append(entry)
return results
Returns:
dict: Inserted audit entry. def append_audit_event(db, event_type, actor, payload, request_ip=None, source="web", max_retries=5, encrypt_fields=None):
"""
Append an audit event, optionally encrypting specific payload fields.
Args:
...
encrypt_fields (list, optional): List of keys in 'payload' to encrypt.
""" """
logs = db["audit_log"] logs = db["audit_log"]
attempts = 0 attempts = 0
@@ -49,15 +63,24 @@ def append_audit_event(db, event_type, actor, payload, request_ip=None, source="
chain_index = int(previous.get("chain_index", 0)) + 1 if previous else 1 chain_index = int(previous.get("chain_index", 0)) + 1 if previous else 1
timestamp = datetime.datetime.utcnow() timestamp = datetime.datetime.utcnow()
# 1. Create the payload dictionary
event_payload = payload or {}
# 2. Encrypt sensitive fields in-place if requested
if encrypt_fields:
encrypt_document_fields(event_payload, encrypt_fields)
entry_payload = { entry_payload = {
"event_type": event_type, "event_type": event_type,
"actor": actor or "system", "actor": actor or "system",
"source": source, "source": source,
"ip": request_ip or "", "ip": request_ip or "",
"payload": payload or {}, "payload": event_payload,
"timestamp": timestamp.isoformat() + "Z", "timestamp": timestamp.isoformat() + "Z",
} }
# 3. Hash the payload (which now contains encrypted values)
entry_hash = _entry_hash(prev_hash, entry_payload) entry_hash = _entry_hash(prev_hash, entry_payload)
entry = { entry = {