1

我有一个敏感属性,必须始终加密,除了在显示期间(不是我的规则,我认为这是矫枉过正,但我​​必须遵守此规则)。此外,用于加密/解密此数据的秘密不得在数据库中或可通过数据库访问。所以目前我有一个用户会话来存储他们的加密密码并在需要时解密这些数据。但是,现在我需要通过加密属性查找记录。我目前使用 ActiveSupport::MessageEncryptor 对属性进行加密/解密。这是我认为我应该完成的方向:

decryptor = ActiveSupport::MessageEncryptor.new(encrypted_password)
Family.where("decryptor.decrypt_and_verify(name) == ?", some_search_name)

显然,这种情况的第一方面不能按原样工作,但我需要一些方法来做到这一点。有任何想法吗?

4

1 回答 1

3

数据库中密码的快速入门

这表明数据库中的加密很困难,除非您仔细考虑过您的威胁模型并了解所有权衡是什么,否则您不应该这样做。老实说,我非常怀疑 ORM 能否在需要加密的地方提供所需的安全性(出于重要的知识原因),而在 PostgreSQL 上,这尤其困难,因为日志文件中可能会泄露密钥。一般来说,您确实需要正确保护密码加密和纯文本,因此您真的不想要一个关系接口,而是一个功能接口,查询在完全不同的权限集下运行。

现在,我无法在您的示例中说明您是否正在尝试保护密码,但如果您是,那完全是错误的方法。我下面的示例将使用 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_pa​​ssword 并在此处使用索引(因为可以在扫描表之前生成匹配的哈希),并且您可以合理地防止 SQL 注入泄露密码哈希。您仍然必须确保这些查询通常不会启用日志记录,并且还有许多其他事情需要考虑,但它让您了解如何最好地管理密码本身。或者,在您的 ORM 中,您可以执行一些会产生 SQL 查询的操作,例如:

  SELECT * FROM users WHERE id = get_userid_by_password($username, $password)

(以上是伪代码,仅用于说明目的。如果您使用组装为文本字符串的原始查询,则表示您要求进行 SQL 注入。)

如果不是密码怎么办?

如果您需要可逆加密,那么您需要更进一步。请注意,在上面的示例中,可以使用索引,因为我只是在加密数据上搜索相等性。搜索未加密的数据意味着索引不可用。如果您索引未加密的数据,那么为什么要首先对其进行加密?此外,解密确实会给处理器带来负担,因此速度会很慢。

在所有情况下,您都需要仔细考虑您的威胁模型,并询问其他漏洞如何降低您的密码安全性。

于 2013-11-04T03:59:36.207 回答