PST-018: A wildcard POSTULO_ALLOWED_HOSTS is not refused, and absolute links in mail and API answers come from the Host header #747
Labels
No labels
accessibility
authentication
breaking change
bug
documentation
enhancement
interface
internationalisation
observability
performance
security
tier
1
tier
2
tier
3
tier/4
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
Postulo/postulo#747
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Found in the security audit of 2026-10-10 (
SECURITY-AUDIT-2026-10-10.md), run againstmainatc30851bfrom 8 to 10 October 2026, as PST-018. Severity: Low.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.
src/postulo/config/settings/base.py(34)Who can do it, and what they need. An unauthenticated stranger, on an instance whose operator set
POSTULO_ALLOWED_HOSTS=*(or a broad suffix) to get a proxy working; nothing in checks.py, prod.py ormanage.py check --deployrefuses it, and the install guidance only says to 'set' it.How it works.
Host: attacker.example.get_reset_password_from_key_urlusesrequest.build_absolute_uri, which trusts the validated Host; with*every host validates.review_url/rendering_urlin API answers,callback_urlregistered with OAuth providers, and the scheduled-job links when POSTULO_PUBLIC_URL is unset.Impact. Account takeover via poisoned password-reset links, but only on a misconfigured instance. On the default and documented configuration (explicit host list, Django's host validation, USE_X_FORWARDED_HOST off, forwarding headers stripped from untrusted peers) this is not reachable, which is why it is low.
Evidence.
Verification (verdict: confirmed; severity after verification: Low). Low is right, conditional on the misconfiguration (wildcard or over-broad host list, with a proxy that passes arbitrary Host through) and on a victim click. Real vector: password-reset email, and sign-up confirmation email for an address the attacker supplies. Drop the claims about scheduled-job links, API review_url/rendering_url and OAuth callback_url as separate vectors. Suggested fix: refuse '*' or a leading-dot catch-all in prod settings or a registered system check, matching the existing refusal of weak keys (#111). When POSTULO_PUBLIC_URL is set, build allauth's absolute URLs from it by overriding get_reset_password_from_key_url and the confirmation URL in AccountAdapter. Also make the install docs say to name the host explicitly.
Suggested remediation. Add a start-up check (beside
refuse_a_weak_key) that refuses*in POSTULO_ALLOWED_HOSTS in production, and preferPOSTULO_PUBLIC_URL(already validated at start-up) over the request host when building links that go into email.Fixed in
9adbcecf6on main.POSTULO_PUBLIC_URL, undoing the rule #747 set #962