You are viewing a plain text version of this content. The canonical link for it is here.
Posted to issues@fineract.apache.org by "Adam Saghy (Jira)" <ji...@apache.org> on 2026/10/18 16:29:00 UTC
[jira] [Updated] (FINERACT-2956) Password reuse check never rejects a previously used password
[ https://issues.apache.org/jira/browse/FINERACT-2956?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel ]
Adam Saghy updated FINERACT-2956:
---------------------------------
Affects Version/s: 1.16.0
> Password reuse check never rejects a previously used password
> -------------------------------------------------------------
>
> Key: FINERACT-2956
> URL: https://issues.apache.org/jira/browse/FINERACT-2956
> Project: Apache Fineract
> Issue Type: Bug
> Components: Security, User Management
> Affects Versions: 1.15.0, 1.16.0
> Environment: develop @ d4ef406954 (bug also present on current develop 999e7a704d), PostgreSQL 18.3, JDK 25, default tenant, no other configuration changes.
> Reporter: Mandib Chaulagain
> Assignee: Mandib Chaulagain
> Priority: Major
> Fix For: 1.16.0
>
> Attachments: image-2026-10-15-21-56-06-601.png, image-2026-10-15-21-56-20-210.png, image-2026-10-15-21-56-30-851.png, image-2026-10-15-21-57-03-681.png, image-2026-10-15-21-57-15-817.png, image-2026-10-15-21-57-33-625.png, image-2026-10-15-21-57-50-357.png, image-2026-10-15-21-57-59-635.png, image-2026-10-15-21-58-31-389.png, image-2026-10-15-21-59-10-033.png
>
>
> Fineract has a setting, {{{}password-reuse-check-history-count{}}}, that is supposed to stop users from setting a password they have used before (added in FINERACT-2005). When the setting is turned on, it does not work. Fineract accepts an old password, and it also accepts the same password the user already has.
> Here are the steps to reproduce:
> # Turn on the check. On {{PUT /v1/configurations/name/\{configName\}}} with configName {{password-reuse-check-history-count}}, send this body ({{"value": 3}} means the last 3 passwords can't be reused):
> !image-2026-10-15-21-56-06-601.png!
> Here's the response body:
> !image-2026-10-15-21-56-20-210.png!
> A GET on the same setting confirms it is enabled with value 3:
> !image-2026-10-15-21-56-30-851.png!
> # Create a test user. On {{POST /v1/users}}, I used the following request body (this is the first time the password is set, call it password A):
> !image-2026-10-15-21-57-03-681.png!
> And got this response (user id 4):
> !image-2026-10-15-21-57-15-817.png!
> # Change to a new password, B, on {{POST /v1/users/4/pwd}} (second time a password is given):
> !image-2026-10-15-21-57-33-625.png!
> The response was 200, which is correct.
> # Reuse the old password A again (third time a password is given):
> !image-2026-10-15-21-57-50-357.png!
> *Actual:* 200, the password is changed.
> !image-2026-10-15-21-57-59-635.png!
> *Expected:* 400 {{error.msg.password.already.used}}, because A is one of the last 3 passwords.
> # Resend the body from step 4, so A is set while A is already the current password (fourth time a password is given):
> !image-2026-10-15-21-58-31-389.png!
> *Actual:* 200 again.
> *Expected:* 400 {{error.msg.password.already.used}}.
> In {{AppUserWritePlatformServiceJpaRepositoryImpl.getCurrentPasswordToSaveAsPreview()}}, the new password is hashed and compared with each old hash using {{String.equals}}. Fineract hashes passwords with bcrypt, which adds a random salt every time, so the same password never produces the same hash string. The comparison can never be true.
> The database shows this. For user 4, the history holds testingasdhjS202@ (set at creation), asdklj@Wfasdsdf989 and testingasdhjS202@, and the current password is testingasdhjS202@. Three of the four hashes are the same password, and still all of them are different strings:
> !image-2026-10-15-21-59-10-033.png!
> There is a second problem. The current password is never checked, because it is only saved to the history table ({{m_appuser_previous_password}}) after it has been replaced.
> The existing unit test does not catch this because it uses a fake encoder that always returns "encoded".
--
This message was sent by Atlassian Jira
(v8.20.10#820010)