我有一个敏感属性,除显示期间外,必须始终加密(这不是我的规则,我认为这太过分了,但我必须遵守此规则)。此外,用于加密/解密此数据的 secret 不得在数据库上或无法通过数据库访问。所以目前我有一个用户 session ,用于存储他们的加密密码并在需要时解密此数据。但是,现在我需要通过加密属性查找记录。我目前使用 ActiveSupport::MessageEncryptor 对属性进行加密/解密。这是我认为我应该去实现的方向:
decryptor = ActiveSupport::MessageEncryptor.new(encrypted_password)
Family.where("decryptor.decrypt_and_verify(name) == ?", some_search_name)
显然,该条件的第一面不能按原样工作,但我需要一些方法来做到这一点。有什么想法吗?
最佳答案
数据库中的密码快速入门
这表明数据库中的加密很难,除非您仔细考虑了您的威胁模型并了解所有权衡是什么,否则您不应该这样做。老实说,我严重怀疑 ORM 能否在需要加密的地方为您提供所需的安全性(出于重要的知识原因),而在 PostgreSQL 上,这尤其困难,因为日志文件中可能会泄露 key 。一般来说,您确实需要妥善保护密码方面的加密文本和纯文本,因此您真的不需要关系界面,而是功能界面,查询在完全不同的集合下运行权限。
现在,我无法在您的示例中判断您是否在尝试保护密码,但如果您在尝试保护密码,那将是完全错误的做法。我下面的示例将使用 MD5。现在我知道 MD5 由于输出相对较短而受到加密社区的反对,但在这种情况下它的优势在于不需要 pg_crypto 支持并且可能比直接攻击密码更强大(在短密码的情况下)字符串,它可能“足够好”,尤其是与其他措施结合使用时)。
现在你要做的是:你想对密码加盐,然后散列它,然后搜索散列值。执行此操作的最有效方法是拥有一个用户表,该表不包含密码,但包含盐,以及一个包含散列密码但不包含用户可访问数据的影子表.影子表将仅限于其所有者,并且该所有者也可以访问用户表。
然后你可以这样写一个函数:
CREATE OR REPLACE FUNCTION get_userid_by_password(in_username text, in_password text)
RETURNS INT LANGUAGE SQL AS
$$
SELECT user_id
FROM shadow
JOIN users ON users.id = shadow.user_id
WHERE users.username = $1 AND shadow.hashed_password = md5(users.salt || $2);
$$ SECURITY DEFINER;
ALTER FUNCTION get_userid_by_password(text, text) OWNER TO shadow_owner;
然后您将不得不转到 SQL 来运行此函数(不要通过您的 ORM)。但是,您可以索引 shadow.hashed_password 并让它与此处的索引一起使用(因为可以在扫描表之前生成匹配的散列),并且您可以合理地防止 SQL 注入(inject)泄露密码散列。您仍然必须确保通常不会对这些查询启用日志记录,并且还有许多其他事项需要考虑,但它让您了解如何最好地管理密码本身。或者,在您的 ORM 中,您可以做一些会产生 SQL 查询的事情,例如:
SELECT * FROM users WHERE id = get_userid_by_password($username, $password)
(以上是伪代码,仅用于说明目的。如果您使用像组装成文本字符串那样的原始查询,那么您就是在请求 SQL 注入(inject)。)
如果不是密码怎么办?
如果您需要可逆加密,那么您需要更进一步。请注意,在上面的示例中,可以使用索引,因为我只是在搜索加密数据的相等性。搜索未加密的数据意味着索引不可用。如果您索引未加密的数据,那么您为什么要首先加密它?此外,解密确实会给处理器带来负担,因此速度会很慢。
在所有情况下,您都需要仔细考虑您的威胁模型并询问其他漏洞如何降低您的密码安全性。
关于postgresql - 如何搜索加密属性?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18086060/