Bloque le SSRF via ntfy_server par validation stricte
Le champ ntfy_server du formulaire /settings était utilisé tel quel dans
urllib.request.urlopen sans aucune validation. Un attaquant pouvait pointer
le serveur vers 169.254.169.254 (metadata AWS/GCP), le réseau interne, ou un
service protégé, et comme la reminder_loop relance l'URL toutes les 5 min le
SSRF devenait persistant. Le header Authorization étant aussi contrôlable,
des requêtes authentifiées vers des services internes étaient possibles.
Ajout de notifications.is_safe_ntfy_server() qui valide : schéma http(s),
absence de credentials embarqués, refus de localhost et de toutes les IP
littérales privées (loopback, link-local, 10/8, 172.16/12, 192.168/16,
fc00::/7, ::1, multicast, réservé). send_ntfy et POST /settings appellent
ce validateur avant toute ouverture de connexion ou persistance ; un serveur
refusé déclenche une redirection /settings?ssrf_error=1 côté UI.
Les tests simulant un échec réseau (127.0.0.1:1) passent sur un host public
injoignable (.invalid), et un nouveau bloc test_security couvre les cas
loopback/private/bad-scheme/credentials et la non-ouverture de socket vers
une IP privée.
💘 Generated with Crush
Assisted-by: Crush:glm-5.2
This commit is contained in:
@ -27,7 +27,7 @@ from models import (
|
||||
from calcul import compute_week, minutes_to_hhmm, hhmm_to_minutes, heures_dues
|
||||
from stats import compute_all_stats
|
||||
from export import export_semaine_ods, export_annee_ods
|
||||
from notifications import reminder_loop, send_ntfy
|
||||
from notifications import reminder_loop, send_ntfy, is_safe_ntfy_server
|
||||
|
||||
logging.basicConfig(
|
||||
level=getattr(logging, load_config().get("log_level", "DEBUG").upper(), logging.DEBUG),
|
||||
@ -624,6 +624,11 @@ def settings_save(
|
||||
rappel_debut_h = max(0, min(23, rappel_debut_h))
|
||||
rappel_fin_h = max(rappel_debut_h + 1, min(24, rappel_fin_h))
|
||||
|
||||
ntfy_server = ntfy_server.strip()
|
||||
if ntfy_server and not is_safe_ntfy_server(ntfy_server):
|
||||
logger.warning("réglages notif rejetés (SSRF): user_id=%r ntfy_server=%r", user_id, ntfy_server)
|
||||
return RedirectResponse("/settings?ssrf_error=1", status_code=303)
|
||||
|
||||
logger.info(
|
||||
"réglages notif mis à jour: user_id=%r ntfy_topic=%r ntfy_server=%r plage=%sh-%sh token=%s",
|
||||
user_id, ntfy_topic, ntfy_server, rappel_debut_h, rappel_fin_h,
|
||||
|
||||
Reference in New Issue
Block a user