PST-017: Single sign-on, login, logout and sign-up accept a next address with an http scheme on an https instance #746

Closed
opened 2026-10-10 10:55:55 +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-017. 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.


  • Severity: Low
  • Category: open-redirect / downgrade
  • Location: third-party: allauth/account/adapter.py (643-676)
  • Found by: CSRF, methods and redirects

Who can do it, and what they need. Anyone who can hand a victim a link to the instance's sign-in page. Only meaningful where HSTS does not apply: an instance reached over a mesh VPN or plain HTTP with POSTULO_SSL_REDIRECT off, or a first visit before HSTS is cached.

How it works.

  1. Attacker sends https://postulo.example/accounts/login/?next=http://postulo.example/settings/.
  2. After sign-in (or immediately when already signed in, because AUTHENTICATED_LOGIN_REDIRECTS is on) allauth validates next with url_has_allowed_host_and_scheme(url, allowed_hosts=...) WITHOUT require_https, so the http:// form of the same host passes.
  3. The browser follows the redirect to plain http; the session cookie is not sent when Secure (so the page shows signed out), but on an instance with POSTULO_SECURE_COOKIES=false the cookie travels in clear.

Impact. Scheme downgrade for the same host; a network attacker on the path could read the session cookie on instances configured without secure cookies. Postulo's own safe_next passes require_https=request.is_secure() and refuses this; the allauth paths do not. Confirmed by scratch test: GET /accounts/login/?next=http://testserver/downgraded/ over HTTPS answered 302 http://testserver/downgraded/.

Evidence.

allauth adapter.py:676 `return url_has_allowed_host_and_scheme(url, allowed_hosts=allowed_hosts)`; postulo/core/redirects.py:20-22 `url_has_allowed_host_and_scheme(target, allowed_hosts={request.get_host()}, require_https=request.is_secure())`.

Verification (verdict: confirmed; severity after verification: Low). Keep it low, and treat it mainly as a gap against the project's own stated invariant ('drops from https to http' is refused; every next goes through safe_next) and against Django's and Postulo's own admin-login behaviour. The auditor's stated impact overreaches in two ways. (a) The cookie is not exposed under default production settings: Secure cookies are not sent on the http hop, HSTS is on, and SECURE_SSL_REDIRECT bounces the hop to https. (b) The 'cookie travels in clear' case needs an HTTPS-served instance with Secure cookies turned off, no HSTS yet cached and a network attacker, and that attacker could strip the first visit anyway. It is a same-host scheme downgrade only; a foreign host is correctly refused. Precise location: the weakness is the missing is_safe_url override in src/postulo/accounts/adapter.py (AccountAdapter), not in a Postulo edit of allauth; SignInView is at src/postulo/accounts/views.py:629, which is accurate. Suggested fix: override AccountAdapter.is_safe_url to call super() and then also refuse an http:// target when context.request.is_secure() (or reimplement with require_https=request.is_secure()). …

Suggested remediation. Override AccountAdapter.is_safe_url in postulo.accounts.adapter to call the parent and additionally require https when request.is_secure() (or delegate to postulo.core.redirects.safe_next's rule), so every next on the instance obeys one rule.

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-017**. 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. --- - **Severity:** Low - **Category:** open-redirect / downgrade - **Location:** `third-party: allauth/account/adapter.py` (643-676) - **Found by:** CSRF, methods and redirects **Who can do it, and what they need.** Anyone who can hand a victim a link to the instance's sign-in page. Only meaningful where HSTS does not apply: an instance reached over a mesh VPN or plain HTTP with POSTULO_SSL_REDIRECT off, or a first visit before HSTS is cached. **How it works.** 1. Attacker sends `https://postulo.example/accounts/login/?next=http://postulo.example/settings/`. 2. After sign-in (or immediately when already signed in, because AUTHENTICATED_LOGIN_REDIRECTS is on) allauth validates `next` with `url_has_allowed_host_and_scheme(url, allowed_hosts=...)` WITHOUT `require_https`, so the http:// form of the same host passes. 3. The browser follows the redirect to plain http; the session cookie is not sent when Secure (so the page shows signed out), but on an instance with POSTULO_SECURE_COOKIES=false the cookie travels in clear. **Impact.** Scheme downgrade for the same host; a network attacker on the path could read the session cookie on instances configured without secure cookies. Postulo's own `safe_next` passes `require_https=request.is_secure()` and refuses this; the allauth paths do not. Confirmed by scratch test: `GET /accounts/login/?next=http://testserver/downgraded/` over HTTPS answered `302 http://testserver/downgraded/`. **Evidence.** ```text allauth adapter.py:676 `return url_has_allowed_host_and_scheme(url, allowed_hosts=allowed_hosts)`; postulo/core/redirects.py:20-22 `url_has_allowed_host_and_scheme(target, allowed_hosts={request.get_host()}, require_https=request.is_secure())`. ``` **Verification** (verdict: confirmed; severity after verification: Low). Keep it low, and treat it mainly as a gap against the project's own stated invariant ('drops from https to http' is refused; every `next` goes through safe_next) and against Django's and Postulo's own admin-login behaviour. The auditor's stated impact overreaches in two ways. (a) The cookie is not exposed under default production settings: Secure cookies are not sent on the http hop, HSTS is on, and SECURE_SSL_REDIRECT bounces the hop to https. (b) The 'cookie travels in clear' case needs an HTTPS-served instance with Secure cookies turned off, no HSTS yet cached and a network attacker, and that attacker could strip the first visit anyway. It is a same-host scheme downgrade only; a foreign host is correctly refused. Precise location: the weakness is the missing is_safe_url override in src/postulo/accounts/adapter.py (AccountAdapter), not in a Postulo edit of allauth; SignInView is at src/postulo/accounts/views.py:629, which is accurate. Suggested fix: override AccountAdapter.is_safe_url to call super() and then also refuse an http:// target when context.request.is_secure() (or reimplement with require_https=request.is_secure()). … **Suggested remediation.** Override `AccountAdapter.is_safe_url` in `postulo.accounts.adapter` to call the parent and additionally require https when `request.is_secure()` (or delegate to `postulo.core.redirects.safe_next`'s rule), so every `next` on the instance obeys one rule.
tiagoagueda added this to the 0.6.0 milestone 2026-10-10 10:57:27 +00:00
Author
Owner

Fixed in de37c4720 on main.

Fixed in de37c4720 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#746
No description provided.