包含无效密码详情的审计消息¶
PCI DSS 要求运营商分析失败的登录尝试,例如,捕获暴力破解或密码填充攻击。为了实现这一点,允许 Keystone 报告失败身份验证尝试中使用的无效密码的详细信息。
问题描述¶
PCI DSS 要求运营商分析失败的登录尝试,例如,捕获暴力破解或密码填充攻击。
Keystone 可以配置为报告失败的身份验证尝试,例如,通过 identity.authenticate 消息,结果为 failure,以便进一步通知运营商执行必要的分析。
但经验表明,一些重复的失败身份验证尝试并非因为存在恶意行为者,而是因为用户的自动化行为出现问题,例如
用户在后端更新了密码,但有一个脚本定期执行(例如,通过 cron),仍然使用旧密码。
用户的自动化程序存在一个错误,导致密码被截断,从而导致每次运行都生成失败的身份验证事件。
用户在 bash 脚本中运行了一个循环,但在
OS_PASSWORD变量中错误地输入了密码,用于 openstack cli,使每次循环运行都生成一个失败的身份验证事件。
Keystone 为此类类似情况生成的通知消息仍然需要像其他消息一样由运营商评估,即使它们与攻击无关。
提议的变更¶
丰富 identity.authenticate 事件,结果为 failure,这些事件是由无效密码提交引起的,并附带提交的(无效)密码的哈希值。相应的哈希函数将返回
相同的哈希值对应相同的无效密码。指示 问题描述示例 中描述的情况。示例
> [generate_partial_password_hash(pwd, **kwargs) for pwd in ["invalidpwd0"] * 4] ['eyWwQ', 'eyWwQ', 'eyWwQ', 'eyWwQ']
不同的哈希值对应变化的密码值。指示例如暴力破解攻击。示例
> [generate_partial_password_hash(pwd, **kwargs) for pwd in ["invalidpwd0", "invalidpwd1", "invalidpwd2", "invalidpwd3"]] ['eyWwQ', '/qk6A', '9sU2g', 'OrVBw']
部分哈希值。使密码的哈希值更能抵抗 彩虹表,尽管仅生成无效密码哈希值。
这种部分哈希值存在可以允许设置监控软件,根据用户和哈希值对事件进行分组,并设置警报限制。例如,以下是选择所有在 1 小时内具有不同哈希值(以及相应变化的密码)的失败身份验证事件的伪代码
SELECT user,
Count(DISTINCT partial_password_hash) AS distinct_hashes
FROM failed_auth_events
WHERE timestamp < now - 3600
GROUP BY user
HAVING distinct_hashes > 1
在此示例中,返回的用户将是可能遭受攻击的用户。相反,多次提交相同无效密码的用户(如 问题描述示例 中所述)不会被返回,因为相应的哈希值不会发生变化。这将帮助运营商仅关注相关事件。
哈希值的包含是可选的,默认情况下禁用。
启用后,类型为 identity.authenticate 且结果为 failure 的事件的有效载荷将丰富 N 个字符的无效密码的哈希值,导致此事件。有关详细信息,请参阅 通知影响。
将提供一个配置选项来配置 N - 返回的无效密码哈希值的字符数。还将提供其他配置选项来保护和确保哈希值(例如,密钥 [1])由哈希函数生成。请参阅 其他部署者影响。
值得强调的是,只有无效密码的哈希值才会出现在通知消息中。identity.authenticate.success 消息,例如,不会包含(有效)密码哈希值。
备选方案¶
其他日志记录。曾考虑添加包含有关身份验证失败的详细信息的其他日志消息。检查应用程序日志是 PCI DSS 异常检测的一种常用方法。但是,keystone 不提供日志稳定性,并且没有用于正确解析日志的库。审计消息已经是一种记录身份验证失败的好方法,它们已经包含有关身份验证失败原因的一些详细信息,并且扩展它们似乎是一个自然的选择。
用于密码身份验证的自定义后端。可以创建一个自定义密码身份验证后端。该后端将继承自 vanilla keystone 后端,捕获身份验证失败并记录消息、发出通知或执行运营商需要的特定操作。
但是,这将破坏其他现有的自定义身份验证插件。将详细信息与已经发出的通知一起使用也很有益,因为已经有处理它们的方法。
实施提议的变更,但不公开哈希值。Keystone 可以将这些哈希值保存在自身内部(例如,在某些内部存储/内存中),并提供配置参数来设置数量的连续变化的哈希值阈值,之后失败的身份验证消息最终将丰富额外的通知文本,以通知运营商可能对用户进行攻击,而无需公开哈希值,而不是用无效密码的哈希值丰富消息。
优点是不暴露/存储任何形式的无效密码哈希值,因此无法对其进行攻击。
缺点是使用相同后端工作的分布式 Keystone 安装将无法汇总整个“全局”部署中的
failed尝试的哈希值,因为每个安装都是独立的,并且没有可以用于此目的的共享内部存储。攻击者可以利用这一事实来避免阈值违规,例如,通过将每个后续请求定向到另一个安装,使每个单独的安装都不会触发通知,即使全局变化的哈希值计数超过了健康阈值。
安全影响¶
如果运营商未在配置中启用该功能,则没有安全影响。
如果启用该功能,它将公开 N 个字符的无效密码的哈希值。这些字符不会通过返回给用户的响应进行通信,而是会通过消息发送。
可以访问消息的运营商可以对无效密码的哈希值进行“攻击”,如果成功,可以为他们提供用户可能提交的许多无效密码值可能性,这些可能性可能进一步用于准备针对该用户的暴力破解攻击。
建议管理员配置返回的哈希字符的最小合理数量 N(请参阅 其他部署者影响) - 这将是保护哈希值的非常有效措施。使用建议实施中的默认哈希算法(请参阅 实施),完整的哈希长度将为 43 个字符。N=5(由管理员提供)可能是非常安全但仍能抵抗碰撞的值的一个例子。
注意:不变化的密码部分哈希值的事件消息并不能保证没有攻击。它可能意味着攻击者使用彩虹表,仅尝试包含报告字符的哈希值的密码。
运营商必须自行决定如何解释这些哈希值,并根据他们的需求和工具选择字符数。
为了最大限度地减少性能影响,建议使用基于哈希的消息身份验证码 [2] HMAC,而不是密钥派生函数 [3]。后者更适合于安全的密码存储和验证。前者也用于行业工具,例如 HashiCorp Vault,用于对审计日志中的敏感数据进行哈希处理 [4]。
通知影响¶
当启用该功能时,一个新的附件 [5] 将添加到事件类型为 identity.authenticate 且结果为 failure 的通知事件中。事件附件名称将为 partial_password_hash,typeURI 将为 mime:text/plain,内容表示无效密码的部分哈希值。该字符串将是 1 个或多个 ASCII 字符。示例
event_type: identity.authenticate
payload:
attachments:
- content: z4Eya
name: partial_password_hash
typeURI: mime:text/plain
outcome: failure
...
其他最终用户影响¶
无
性能影响¶
当启用该功能时,对无效密码进行哈希处理将占用最少的计算资源,具体取决于使用的配置参数(请参阅 其他部署者影响)。
# default configuration
python -m timeit -n 1000000 \
-s "from keystone.common.password_hashing import generate_partial_password_hash" \
"generate_partial_password_hash('invalidpwd0', 'strongsalt', secret_key='secret_key', max_chars=5, \
hash_function='sha256')"
1000000 loops, best of 5: 2.43 usec per loop
# using HMAC with sha512
python -m timeit -n 1000000 \
-s "from keystone.common.password_hashing import generate_partial_password_hash" \
"generate_partial_password_hash('invalidpwd0', 'strongsalt', secret_key='secret_key', max_chars=5, \
hash_function='sha512')"
1000000 loops, best of 5: 2.98 usec per loop
对于哈希函数实现,建议使用 Python 标准库的 hmac [6] 模块,利用 hashlib [7],即使 cryptography 包在 Keystone 中可用。因为 hashlib 在 建议的加密哈希算法 中表现出更好的性能 [8]。
# hashlib.scrypt with work factor similar to the default configuration
python -m timeit -n 1000000 -u usec \
-s "import hmac" \
"hmac.digest(b'secret_key', b'invalid_password', 'sha256')"
1000000 loops, best of 5: 0.879 usec per loop
# hashlib.scrypt with work factor similar to the default configuration
python -m timeit -n 1000000 -u usec \
-s "from cryptography.hazmat.primitives import hashes, hmac" \
"h = hmac.HMAC(b'secret_key', hashes.SHA256()); h.update(b'invalid_password'); h.finalize()"
1000000 loops, best of 5: 2.16 usec per loop
其他部署者影响¶
如果运营商不需要该功能,则没有影响。
如果需要该功能,则 security_compliance 部分中提供以下选项
开发人员影响¶
无
实现¶
可能的实现 - 932423:支持发出无效密码的部分哈希值
负责人¶
- 主要负责人
stanislav-z
- 其他贡献者
bbobrov
工作项¶
TODO
依赖项¶
无
文档影响¶
需要记录选项和安全影响。
参考资料¶
https://haveibeenpwned.com/API/v3#SearchingPwnedPasswordsByRange - 用于按 SHA-1 或 NTLM 哈希前缀(5 个字符)搜索泄露密码的服务 - 由于哈希算法不同,因此无法用于提议的实现。
https://opendev.org/openstack/keystoneauth/commit/2b305a718cb84edbdd977c26ca7e4134a3083c57,https://opendev.org/openstack/keystoneauth/commit/ccf6cb79033b2083d9177823094f7836eb68ae0d -
keystoneauth使用 SHA256 对调试输出中的敏感数据进行哈希处理。https://compare-hashing-algorithms.mojoauth.com/hmac-sha256-vs-scrypt/ - HMAC-SHA256 与 scrypt
脚注