PST-071: /healthz tells unauthenticated callers the exact Postulo version and database state #800

Closed
opened 2026-10-10 10:56:41 +00:00 by tiagoagueda · 1 comment
Owner

Found in the security audit of 2026-10-10 (SECURITY-AUDIT-2026-10-10.md), run against main at c30851b from 8 to 10 October 2026, as PST-071. Severity: Informational.

The text below is the audit's entry, unchanged apart from links to the other issues it names. The finding was produced by automated review and checked by a second automated pass; re-check it before acting on it.


  • Severity: Informational
  • Category: information-disclosure
  • Location: src/postulo/core/views.py (50-58)
  • Found by: CSRF, methods and redirects, administrator surface and plugins, information disclosure, ownership review of core, secrets and cryptography

Who can do it, and what they need. Any stranger on the internet who finds the address.

How it works.

  1. GET /healthz with no session.
  2. healthz() returns {"status": "ok", "database": "ok", "version": installed_version()}.
  3. The attacker matches the version against the CHANGELOG's Security entries to pick a known vulnerability before the operator upgrades.

Impact. Version fingerprinting helps target an unpatched instance; the endpoint is intentional (container HEALTHCHECK) and reveals nothing personal.

Verification (verdict: confirmed; severity after verification: Informational). No correction to file or line. Precondition: the stranger must send a Host the instance answers to (HealthProbeMiddleware only waives that from 127.0.0.1), which any browser does. The disclosure is a recorded design decision (#172, base.html comment) and the wiki documents the endpoint; the one observation to add is that #172's stated aim of keeping the version off pages a crawler reads is only partly met while /healthz is open.

Suggested remediation. Return the version only to loopback probes (HealthProbeMiddleware already identifies them) or to signed-in administrators, and keep the public answer to status and database.

Also covered by this issue.

  • /healthz tells unauthenticated strangers the exact Postulo version, which the footer deliberately hides from them (src/postulo/core/views.py (50-58); Informational). Target selection for a future disclosed vulnerability; contradicts the stated intent in base.html that 'the version is for people signed in' (which admits '/healthz still says it, to a monitor').
  • Installed version disclosed to unauthenticated callers by /healthz (and Python/Django versions plus record counts by /metrics when enabled without a token) (src/postulo/core/views.py (50-58); Informational). Precise fingerprinting of an instance that otherwise says nothing to strangers (the threat model's first row), aiding targeted exploitation after a disclosure. No direct data exposure.
  • /healthz discloses the exact running version and database state to unauthenticated callers (src/postulo/core/views.py (50-58); Informational). Version fingerprinting; no data exposure. Documented behaviour with its own tests (test_health_probe_host.py, test_health_redirect.py).
  • /healthz tells unauthenticated callers the exact running version (and /metrics without a token also lists version, Python and Django) (src/postulo/core/views.py (50-58); Informational). Fingerprinting an instance and matching it to published advisories without signing in; a minor aid to a stranger, and already acknowledged in the template comment.
Found in the security audit of 2026-10-10 (`SECURITY-AUDIT-2026-10-10.md`), run against `main` at `c30851b` from 8 to 10 October 2026, as **PST-071**. Severity: **Informational**. The text below is the audit's entry, unchanged apart from links to the other issues it names. The finding was produced by automated review and checked by a second automated pass; re-check it before acting on it. --- - **Severity:** Informational - **Category:** information-disclosure - **Location:** `src/postulo/core/views.py` (50-58) - **Found by:** CSRF, methods and redirects, administrator surface and plugins, information disclosure, ownership review of core, secrets and cryptography **Who can do it, and what they need.** Any stranger on the internet who finds the address. **How it works.** 1. GET /healthz with no session. 2. healthz() returns {"status": "ok", "database": "ok", "version": installed_version()}. 3. The attacker matches the version against the CHANGELOG's Security entries to pick a known vulnerability before the operator upgrades. **Impact.** Version fingerprinting helps target an unpatched instance; the endpoint is intentional (container HEALTHCHECK) and reveals nothing personal. **Verification** (verdict: confirmed; severity after verification: Informational). No correction to file or line. Precondition: the stranger must send a Host the instance answers to (HealthProbeMiddleware only waives that from 127.0.0.1), which any browser does. The disclosure is a recorded design decision (#172, base.html comment) and the wiki documents the endpoint; the one observation to add is that #172's stated aim of keeping the version off pages a crawler reads is only partly met while /healthz is open. **Suggested remediation.** Return the version only to loopback probes (HealthProbeMiddleware already identifies them) or to signed-in administrators, and keep the public answer to status and database. **Also covered by this issue.** - **/healthz tells unauthenticated strangers the exact Postulo version, which the footer deliberately hides from them** (`src/postulo/core/views.py` (50-58); Informational). Target selection for a future disclosed vulnerability; contradicts the stated intent in base.html that 'the version is for people signed in' (which admits '/healthz still says it, to a monitor'). - **Installed version disclosed to unauthenticated callers by /healthz (and Python/Django versions plus record counts by /metrics when enabled without a token)** (`src/postulo/core/views.py` (50-58); Informational). Precise fingerprinting of an instance that otherwise says nothing to strangers (the threat model's first row), aiding targeted exploitation after a disclosure. No direct data exposure. - **/healthz discloses the exact running version and database state to unauthenticated callers** (`src/postulo/core/views.py` (50-58); Informational). Version fingerprinting; no data exposure. Documented behaviour with its own tests (test_health_probe_host.py, test_health_redirect.py). - **/healthz tells unauthenticated callers the exact running version (and /metrics without a token also lists version, Python and Django)** (`src/postulo/core/views.py` (50-58); Informational). Fingerprinting an instance and matching it to published advisories without signing in; a minor aid to a stranger, and already acknowledged in the template comment.
tiagoagueda added this to the 1.0.0 milestone 2026-10-10 10:58:06 +00:00
Author
Owner

Fixed in 95617f5e5 on main.

Fixed in 95617f5e5 on main.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#800
No description provided.