数据库中密码的快速入门
这表明数据库中的加密很困难,除非您仔细考虑过您的威胁模型并了解所有权衡是什么,否则您不应该这样做。老实说,我非常怀疑 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_password 并在此处使用索引(因为可以在扫描表之前生成匹配的哈希),并且您可以合理地防止 SQL 注入泄露密码哈希。您仍然必须确保这些查询通常不会启用日志记录,并且还有许多其他事情需要考虑,但它让您了解如何最好地管理密码本身。或者,在您的 ORM 中,您可以执行一些会产生 SQL 查询的操作,例如:
SELECT * FROM users WHERE id = get_userid_by_password($username, $password)
(以上是伪代码,仅用于说明目的。如果您使用组装为文本字符串的原始查询,则表示您要求进行 SQL 注入。)
如果不是密码怎么办?
如果您需要可逆加密,那么您需要更进一步。请注意,在上面的示例中,可以使用索引,因为我只是在加密数据上搜索相等性。搜索未加密的数据意味着索引不可用。如果您索引未加密的数据,那么为什么要首先对其进行加密?此外,解密确实会给处理器带来负担,因此速度会很慢。
在所有情况下,您都需要仔细考虑您的威胁模型,并询问其他漏洞如何降低您的密码安全性。