我该如何在postgresql中进行密码哈希?

64
我需要在postgresql上对一些密码进行哈希加盐,但我找不到任何相关的文档来实现这一点。
那么,我应该如何在postgresql中哈希(带有某些盐)密码呢?
3个回答

101

我问这个问题已经有一段时间了,现在我对加密理论更加熟悉,所以这是更现代的方法:

推理

  • 不要使用md5。不要使用sha系列快速哈希算法的单个循环。快速哈希会帮助攻击者,因此您不想这样做。
  • 相反,使用资源密集型哈希(如bcrypt)。Bcrypt经过时间考验,并可扩展为未来可靠的选项。
  • 不要费心去自己生成盐,否则可能会损害您的安全性或可移植性,请依赖于gen_salt()自行生成唯一盐。
  • 总的来说,不要傻到尝试编写自己的加密算法,只需使用聪明人提供的就可以了。

Debian/Ubuntu安装软件包

sudo apt-get install postgresql   // (of course)
sudo apt-get install postgresql-contrib libpq-dev   // (gets bcrypt, crypt() and gen_salt())
sudo apt-get install php5-pgsql   // (optional if you're using postgresql with php)

在你的数据库中激活postgresql中的crypt()和bcrypt()

// Create your database first, then:
cd `pg_config --sharedir` // Move to the postgres directory that holds these scripts.
echo "create extension pgcrypto" | psql -d yOuRdATaBaSeNaMe // enable the pgcrypo extension

在查询中使用crypt()和gen_salt()

使用crypt()和gen_salt()与现有哈希值进行比较:

select * from accounts where password_hash = crypt(:pass, password_hash);
//(note how the existing hash is used as its own individualized salt)

使用随机盐值对 :password 进行哈希:

insert into accounts (password) values crypt(:password, gen_salt('bf', 8));
//(the 8 is the work factor)

使用Php中的bcrypt哈希略为可取

在php 5.5及以上版本中有password_*函数可允许使用bcrypt进行极其简单的密码哈希(终于!),对于低版本的php也有一个向后兼容的库。通常情况下会回退到包装Linux系统调用来降低CPU使用率,但您可能需要确保它已经安装在您的服务器上。参见:https://github.com/ircmaxell/password_compat(需要php 5.3.7+)

注意日志记录

请注意,在pg_crypto中,密码在从浏览器到php再到数据库的传输过程中始终以明文形式存在。这意味着,如果您在数据库日志中不小心处理,例如设置了postgresql慢查询日志,则可能捕获并记录正在进行的登录查询中的密码。以纯文本形式

总结

如果可以,请使用php bcrypt,它将缩短密码未哈希的时间。尽量确保您的Linux系统已经安装了bcrypt的crypt(),这样才能具有良好性能。强烈建议升级至至少php 5.3.7+,因为php在5.3.0到5.3.6.9版本中的实现略有缺陷,并且在php 5.2.9及更低版本中不合适地回退到破损的DES而没有任何警告。

如果您想/需要在postgresql中进行哈希,请安装bcrypt,因为默认安装的哈希值已经过时且易受攻击(例如md5)。

以下是有关此主题的更多阅读参考:


2
那么,使用pgcrypto进行哈希处理比在应用程序端进行更好吗?通常情况下,哈希处理、GUID生成等例行工作应该由pg而不是应用程序完成吗?谢谢! - paulkon
1
我已经在上面编辑了更详细的答案。由于pg_crypto要求密码的明文命中数据库查询,可能会出现意外查询日志记录的问题,因此如果可以实现,我建议首先尝试使用php中的password_hash()。Bcrypt是密码哈希的最新技术,因此它比其他选项(无论是在postgresql还是php中)都更好。Bcrypt哈希是有意设计为资源密集型的,因此如果您在php中使用它,请尝试从crypt()中获取bcrypt以减少服务器的资源使用。 - Kzqai
不要使用md5,它已经被破解了。Postgres听到“使用md5”是因为它用它来哈希密码... - xenoterracide
1
我在阅读这里关于日志问题的内容后,突然惊慌失措并重写了一堆代码。然后我意识到我正在使用参数化查询,这意味着我的数据库日志中没有明文内容。特别是在使用pgp加密时,我还需要将我的公钥/私钥字符串作为参数传递,以避免记录日志。 - deltree
1
Postgres 11+拥有scram-sha-256密码方案(来自RFC 7677)。由于它使用了SCRAM,在某些情况下,它可能是PHP的password_*函数的替代方案。 - Code4R7
据我所知,sha-256是一种快速哈希算法,但对于密码来说,你需要使用慢速哈希算法,以便在攻击者获取到数据库数据的副本时能够减缓攻击速度。 - Kzqai

15

应用程序应使用诸如bcrypt或pbkdf2之类的密钥派生函数来哈希其密码。这里提供了更多关于安全密码存储的信息

...但有时候数据库仍需要加密功能。

您可以使用pgcrypto来访问sha256,它是sha2家族的成员。请记住,sha0、sha1、md4和md5非常不安全,绝不能用于密码哈希。

下面是一种不错的密码哈希方法:

digest("salt"||"password"||primary_key, "sha256")

盐值应该是一个大的随机生成的值。必须保护这个盐值,因为在盐值恢复之前无法破解哈希。如果您将盐值存储在数据库中,则可以使用SQL注入获取它以及密码哈希。使用主键连接可以防止两个人具有相同的密码哈希,即使他们使用相同的密码。当然,这个系统可以改进,但比我看到的大多数系统都要好得多。

通常最好在应用程序中进行哈希处理,然后再将其传递给数据库。这是因为查询可能会出现在日志中,如果数据库服务器被拥有,则可以启用日志记录以获取明文密码。


1
是的,我有点晚了。因为你回答得更详细,所以我删除了我的答案。 ;) - Tagore Smith
@T Duncan Smith,谢谢你,我给了你一些积分,因为你是一个好的SO成员。 - rook
嗯,记录问题是一个很好的观点,但出于实际原因,我想能够运行SQL语句来取消个人化密码(以及其他个人信息),以便发布已清理的数据库。 - Kzqai
7
pgcrypto页面上的文档非常好,可以澄清为什么这是一个非常愚蠢的散列方式。实际上,这是一场灾难的配方。请改用crypt函数,并选择'bf'哈希。更多信息请参见http://codahale.com/how-to-safely-store-a-password/,包括如何使用几十亿个加盐哈希进行自定义破解。 - nealmcb
@nealmcb 的最后一条评论是正确的,但他没有解释原因,留给你在 PGSQL 文档中查找答案。原因是如果在数据库上计算哈希值,则密码会通过网络发送到数据库,可能会被拦截。可以通过加密数据库连接来缓解此问题,但我认为最好避免在网络上发送明文密码,因为每次发送都是黑客拦截的潜在机会。 - Nate C-K
显示剩余4条评论

10

3
是的,pgcrypto看起来就是我要找的东西,但我很难弄清楚它的用法,例子中的用法是否意味着我不必将我的salt硬编码进哈希值?也就是说,我是否不再需要提供自己的salt数据,例如:"update account set pswhash = crypt('global salt' || 'new password' || 'user created date', gen_salt('sha256')) where account_id = 5"?还是添加salt仍然是一个手动过程? - Kzqai
2
使用md5算法,没有任何迭代计数(适应随时间增加的哈希速度)是灾难的配方。相反,使用'bf':gen_salt('bf')。更多信息,请参见http://codahale.com/how-to-safely-store-a-password/,其中包括如何使用几十亿个盐哈希进行自定义破解。 - nealmcb
4
@Tchalvak 正确 - 您不再需要提供自己的盐数据。实际上,gen_salt函数还编码了算法,应该是'bf',就像我上面提到的那样 - 更多信息请参阅参考文献。由于使用了“bf”算法,这个答案比rook的答案好得多。 - nealmcb
1
这是正确的答案。你不应该使用digest函数来加密密码,它不够安全。只需确保使用Blowfish算法而不是MD5即可。 - GetFree

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接