Mandib Chaulagain created FINERACT-2956:
-------------------------------------------
Summary: 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
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
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)