Issue 7468 - RFE - HIBP password breach validation#7492
Conversation
There was a problem hiding this comment.
Hey - I've found 2 issues, and left some high level feedback:
- In the add and non-root modify paths,
hibp_check_passwordis called on all values without checkingslapi_is_encoded, which could lead to hashed or otherwise encoded passwords being sent to the breach service; mirroring the rootpw handling by skipping encoded values would avoid this. - The breach-checking logic is duplicated across
op_shared_add,op_shared_allow_pw_change, and the rootpw handling inop_shared_modify; consider extracting a shared helper that takes a value list and policy to reduce repetition and keep future behavior changes consistent. - There is a stray tab-based indentation change in
op_shared_allow_pw_changearound the "This is an internal operation" comment; aligning this with the existing space-based indentation would keep the file formatting consistent.
Prompt for AI Agents
Please address the comments from this code review:
## Overall Comments
- In the add and non-root modify paths, `hibp_check_password` is called on all values without checking `slapi_is_encoded`, which could lead to hashed or otherwise encoded passwords being sent to the breach service; mirroring the rootpw handling by skipping encoded values would avoid this.
- The breach-checking logic is duplicated across `op_shared_add`, `op_shared_allow_pw_change`, and the rootpw handling in `op_shared_modify`; consider extracting a shared helper that takes a value list and policy to reduce repetition and keep future behavior changes consistent.
- There is a stray tab-based indentation change in `op_shared_allow_pw_change` around the "This is an internal operation" comment; aligning this with the existing space-based indentation would keep the file formatting consistent.
## Individual Comments
### Comment 1
<location path="ldap/servers/slapd/add.c" line_range="674-677" />
<code_context>
present_values = attr_get_present_values(attr);
+#ifdef ENABLE_HIBP
+ /* Check all passwords against breach database */
+ if (pwpolicy->pw_check_breach) {
+ for (size_t i = 0; present_values[i] != NULL; i++) {
+ const char *pwd = slapi_value_get_string(present_values[i]);
+ int breach_count = hibp_check_password(pwd, pwpolicy);
+ if (breach_count > 0) {
+ slapi_log_err(SLAPI_LOG_WARNING, "op_shared_add",
</code_context>
<issue_to_address>
**🚨 issue (security):** Avoid sending hashed passwords to the breach checker in add operations
In `op_shared_add`, the breach check runs over all present values without skipping already-encoded passwords (e.g. pre-hashed `userPassword`). This can leak reusable hashed secrets to the HIBP backend and may misbehave if `hibp_check_password` expects cleartext. Please align with the rootpw/modify logic and skip values where `slapi_is_encoded(pwd)` is true before calling `hibp_check_password`.
</issue_to_address>
### Comment 2
<location path="ldap/servers/slapd/modify.c" line_range="1311-1316" />
<code_context>
+#ifdef ENABLE_HIBP
+ /* Check all passwords against breach database before any other checks */
+ if (pwpolicy->pw_check_breach && mod->mod_bvalues) {
+ Slapi_Value **breach_vals = NULL;
+ valuearray_init_bervalarray(mod->mod_bvalues, &breach_vals);
+ if (breach_vals) {
+ for (size_t i = 0; breach_vals[i] != NULL; i++) {
+ const char *pwd = slapi_value_get_string(breach_vals[i]);
+ int breach_count = hibp_check_password(pwd, pwpolicy);
+ if (breach_count > 0) {
</code_context>
<issue_to_address>
**🚨 issue (security):** Skip encoded passwords when running breach checks during modify operations
In `op_shared_allow_pw_change`, the breach check runs on all `mod->mod_bvalues` without skipping encoded values. This can send pre-hashed `userPassword` values to `hibp_check_password`, which both misuses the API (it expects cleartext) and risks leaking reusable hash material to the external service. Please add a `!slapi_is_encoded(pwd)` guard before calling `hibp_check_password`, consistent with the rootpw handling.
</issue_to_address>Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.
|
@jchapma I think this needs to be rebased. It's not building. |
f2b947f to
7d8cfd2
Compare
|
Congratulations! One of the builds has completed. 🍾 You can install the built RPMs by following these steps:
Please note that the RPMs should be used only in a testing environment. |
7d8cfd2 to
39f3bd5
Compare
| #ifndef ENABLE_HIBP | ||
| if (apply && value && strcasecmp(value, "on") == 0) { | ||
| slapi_log_err(SLAPI_LOG_WARNING, "config_set_pw_breach_check", | ||
| "HIBP breached password checking not enabled - passwordBreachCheck has no effect\n"); |
There was a problem hiding this comment.
I think we should reject on with an LDAP error when HIBP support is unavailable.
An admin would see the setting as enabled and reasonably believe breached-password protection is active. I don't think logging the warning is visible enough.
| for (size_t i = 0; breach_vals[i] != NULL; i++) { | ||
| const char *pwd = slapi_value_get_string(breach_vals[i]); | ||
| if (pwd && !slapi_is_encoded((char *)pwd)) { | ||
| int breach_count = hibp_check_password(pwd, pwpolicy); |
There was a problem hiding this comment.
If we call HIBP before the ACI/password-change checks, I think, an unauthorized user can trigger outbound HIBP requests for chosen values before the server rejects the modify. The add path already checks ACI first; modify should do the same, IMO
There was a problem hiding this comment.
You are correct, we should do the HIBP check after ACI checks, thanks
|
|
||
| #ifdef ENABLE_HIBP | ||
| /* Check all passwords against breach database before any other checks */ | ||
| if (pwpolicy->pw_check_breach && mod->mod_bvalues) { |
There was a problem hiding this comment.
IIUC, it will be checked even for LDAP_MOD_DELETE of the userPassword attribute and that seems wrong. We should be able to delete breached password
| } else if (!strcasecmp(attr_name, "passwordBreachDbUrl")) { | ||
| if ((sval = attr_get_present_values(attr))) { | ||
| pwdpolicy->pw_breach_db_url = | ||
| slapi_ch_strdup(slapi_value_get_string(*sval)); |
There was a problem hiding this comment.
Do we free it? I'm not fully sure who owns it (I wasn't able to find)
| if (config_value_is_null(attrname, value, errorbuf, 0)) { | ||
| value = NULL; | ||
| } | ||
|
|
There was a problem hiding this comment.
I wonder if we should validate passwordBreachDbUrl to require https://, normalize/require a trailing / and ideally also set libcurl allowed protocols to HTTPS only with CURLOPT_PROTOCOLS_STR. CURLOPT_DEFAULT_PROTOCOL can make scheme-less URLs default to HTTPS, but for this security feature I’d still reject scheme-less input rather than guess. WDYT?
There was a problem hiding this comment.
Thats a good idea, currently there is no validation. Thanks
39f3bd5 to
5e676b3
Compare
| pwdpolicy->pw_check_dict = g_pwdpolicy->pw_check_dict; | ||
| pwdpolicy->pw_dict_path = g_pwdpolicy->pw_dict_path; | ||
| pwdpolicy->pw_check_breach = g_pwdpolicy->pw_check_breach; | ||
| pwdpolicy->pw_breach_db_url = g_pwdpolicy->pw_breach_db_url; |
There was a problem hiding this comment.
I think this might accidentally hand ownership of the global breach URL to the local policy. Since delete_passwdPolicy() frees pw_breach_db_url for local policies, could we duplicate this value here or otherwise avoid freeing a borrowed global pointer?
There was a problem hiding this comment.
Ah yes, this is a bug. The global pw_breach_db_url is asigned directly and will be freed, possibly causing a double free. Duplicating pw_breach_db_url.
| assert value.lower() == 'off' | ||
|
|
||
| # Verify setting passwordBreachCheck to 'on' | ||
| inst.config.set('passwordBreachCheck', 'on') |
There was a problem hiding this comment.
I think this may fail when the server is built without HIBP support, which is the default.
There was a problem hiding this comment.
I just tried it and this test does fail when HIBP is not enabled, I will add a try/except to skip the test the HIBP assertions when the server returned UNWILLING_TO_PERFORM. Apologies I though I had tested this
| * Check password against breach database after ACI validation. | ||
| */ | ||
| if (!SLAPI_IS_MOD_DELETE(mod->mod_op) && | ||
| !pw_is_pwp_admin(pb, pwpolicy, PWP_ADMIN_OR_ROOTDN) && |
There was a problem hiding this comment.
The add path checks all users, and the commit message says admin and non-admin password changes are validated, but this condition lets admins bypass HIBP during modify. I think we might have an inconsistancy unless I miss something...
There was a problem hiding this comment.
Good catch, there was an inconsistency. The modify path allowed admins to bypass HIBP, but add did not. Added the admin bypass check to add.c
| {CONFIG_PW_BREACH_TIMEOUT_ATTRIBUTE, config_set_pw_breach_timeout, | ||
| NULL, 0, | ||
| (void **)&global_slapdFrontendConfig.pw_policy.pw_breach_db_timeout, | ||
| CONFIG_INT, NULL, "5", NULL}, |
There was a problem hiding this comment.
This default is 5... While the policy/client defaults are 10. Should they be align?
There was a problem hiding this comment.
Updated to match HIBO default
Description: Integrate the HIBP HTTP client into password policy. Adds config, schema, and password validation logic to check passwords against the HIBP breach database during password add and modify operations. Depends on the HIBP client PR. - Schema: passwordBreachCheck, passwordBreachDbUrl, passwordBreachDbTimeout - Config: Config setters in libglobs.c - Validation: Check passwords on add/modify for both admin and non-admin users - rootDN: Also validates nsslapd-rootpw changes against breach database Dependencies: - Issue 7468 - RFE - Add HIBP HTTP client Relates: 389ds#7468 Reviewed by:
c6815c1 to
677d7bb
Compare
Description:
Integrate the HIBP HTTP client into password policy. Adds config, schema, and password validation logic to check passwords against the HIBP breach database during password add and modify operations. Depends on the HIBP client PR.
Relates: #7468
Reviewed by:
Summary by Sourcery
Integrate breached password checking into the directory server’s password policy using the HIBP client, including configuration, schema wiring, and runtime enforcement for password operations.
New Features:
Enhancements: