在数据库中存储用户和密码

10 投票
2 回答
3416 浏览
提问于 2025-04-16 17:57

我正在开发一个软件,用户需要输入用户名和密码。经过验证后,用户可以访问一些半公开的服务,还可以加密一些只有他们自己能访问的文件。

用户的信息必须原封不动地保存,如果可以的话。在验证后,用户和密码会在软件运行期间保存在内存中(我也不知道这样是否合适)。

我的问题是,应该如何在一个可能不安全的数据库中存储这个用户名和密码的组合呢?

我不太明白应该暴露哪些信息。

假设我创建了一个增强的密钥,像这样:

salt = random 32 characters string (is it okay?)
key = hash(usr password + salt)
for 1 to 65000 do
  key = hash(key + usr password + salt)

我应该把[明文用户名]、[增强密钥]和[盐值]存储在数据库里吗?

另外,我该用什么来加密文件(用AES或Blowfish)并且每次都用新密码?我是否应该生成一个新的盐值,并使用(程序内存中存储的密码 + 盐值)来创建一个新的增强密钥?在这种情况下,如果我把加密后的文件存储在数据库中,可能只需要存储盐值。

文件只有在有人能生成密钥的情况下才能解密,但他们不知道密码,对吧?

我使用Python和PyCrypto,但这并不重要,给个通用的例子就可以了。我看过一些类似的问题,但它们并不是很明确。

非常感谢!

2 个回答

2

如果你给每个用户使用不同的盐值(salt),那么你需要把这些盐值存储起来(最好放在一个不同的地方)。如果你给每个用户都用同样的盐值,那你可以直接把它写死在你的应用里,但这样安全性就会差一些。
如果你不保存盐值,就没办法把某个密码和数据库里的密码进行匹配。

盐值的目的是为了让暴力破解或者字典攻击变得更困难。这就是为什么把盐值单独存储会更安全,避免有人同时拥有哈希密码和对应的盐值。

10

加密技术很难掌握,能提问说明你在认真对待这个问题。

存储密码: 密码应该用一种叫做 密钥拉伸算法 的方法进行处理。通常来说,你最好使用现成的库,而不是自己去实现。密钥拉伸算法的设计是为了消耗计算机的处理能力,所以用好的C语言代码来评估它们是个不错的主意。如果你在使用Linux系统,并且有 glibc,可以使用 crypt.crypt 模块(可以查看 man crypt 来了解更多):

import crypt
encrypted = crypt.crypt(password, '$6$' + salt + '$')

这个方法会返回一个ASCII字符串,你可以安全地把它存储在数据库里。($6$ 是一个glibc的扩展,它使用基于SHA-512的密钥拉伸函数。如果你没有这个扩展,就不要使用 crypt.crypt)。 (补充:这个算法和你在问题中提到的很相似。不过,最佳实践通常是让库来处理这些事情,而不是自己动手。)

加密文件: 不要自己去做。安装GnuPG(或者scrypt、bcrypt、ncrypt等工具)。自己设计加密文件的方法很容易出错。这些工具会使用正确的密钥派生函数、认证哈希和加密模式,而且不需要额外的配置。它们不是Python库,而是可执行文件,所以你需要写一个包装器来使用 subprocess 模块。

内存中的密码: 不要这样做。一旦你检查了用户的密码和你的密码数据库,应该用密钥派生函数把密码转换成一个密钥。然后你可以用这个密钥来解锁加密的文件,但你就不能再用它来找回原来的密码了。

撰写回答