PST-092: The VAPID public key is derived from the secret key and handed to every signed-in member #821
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#821
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-092. 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.
src/postulo/plugins/browser/webpush.py(91-105)Who can do it, and what they need. Any account holder on the instance (or anyone who obtains the HTML of the 'Browser' connection page). Precondition: the instance runs on a guessable key - POSTULO_ALLOW_WEAK_SECRET_KEY=true with a short/dictionary key, the dev settings, or a key that passes the length/variety check but is a …
How it works.
Impact. On an instance whose operator used the documented weak-key override, a single member can escalate to every account and to every stored third-party credential. On a strong key there is no practical impact, which is why this is low.
Verification (verdict: confirmed; severity after verification: Informational; first-pass claim: Low). Keep the observation and drop the impact chain. The claim that SECRET_KEY lets an attacker 'forge Django session cookies / password-reset tokens for any account including administrators' does not hold here: sessions are database-backed with random ids, and reset tokens need the victim's password hash. What a recovered SECRET_KEY realistically gives is forged consent
statevalues and forged flash-message cookies, plus decryption of stored secrets for anyone who also holds a database copy. The VAPID key is not an additional oracle for SECRET_KEY, since the signed messages cookie and the consent state are available to every member at the same per-guess cost. …Suggested remediation. Generate the VAPID key pair once with ec.generate_private_key(), store the private half in an encrypted SiteSettings column (same Fernet store as the mail password) and publish only that public key; or derive it with HKDF from the key material plus a random per-instance salt kept in the database. Either way the published point stops being a pure function of SECRET_KEY. …
Fixed in
9d819450fon main.