SEC2-002: Deactivating an account and changing someone's username under *People* need no fresh proof, unlike the neighbouring actions #944
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#944
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 on 2026-10-16 in a read-only security review of
postuloat56fc9d531, checked by a second, independent pass.Severity: Low · Category: Authentication/Authorization · Reference: CWE-306 (Missing Authentication for Critical Function); OWASP A07:2021 (Identification and Authentication Failures)
Location:
src/postulo/core/server_views.py:280-302(PersonActiveView.post)src/postulo/core/server_views.py:438-477(PersonUsernameView)src/postulo/accounts/reauth.py:42-50(reauthentication_required, with itsallow_getoption)Issue
#740 put
reauthentication_requiredon appointing/demoting an administrator (line 245), deleting an account (line 334) and issuing a recovery link (line 384). Two actions on the same page were left out:Deactivation ends every session of the target (
ModelBackend.get_userrefuses an inactive user) and refuses their API tokens (api/auth.py:70), so it is a denial of access to a member; a username change alters a sign-in identifier and what every other member sees the person as. The only guard is a session that is signed in as staff, which is exactly the left-open-browser case #740 was about.Impact
Someone with an administrator's unattended or stolen session can lock any member out (deactivate) or rename them, including the operator, without being asked for a password or second factor. Reversible, so Low; but it is the same precondition and page as #740.
Fix
Add
@method_decorator(reauthentication_required)toPersonActiveView.post.PersonUsernameViewis anUpdateView, so decorate itsdispatchwithreauthentication_required(allow_get=True)(the project's wrapper supportsallow_get,accounts/reauth.py:50) or overridepost. Done whentests/test_hardening_740.pygains the two routesserver:person_activeandserver:person_usernamebeside the existing three, showing that an administrator whose last authentication is older thanACCOUNT_REAUTHENTICATION_TIMEOUTis sent to reauthenticate and nothing changed.Related
#740 (closed) - covered demotion, deletion and recovery links; these two actions on the same page were not included, so this is an incomplete fix. #741 (closed) - SSO freshness, already handled by the wrapper.