在django中安全地存储加密的凭证

ask*_*tor 13 python security encryption django credentials

我正在开发一个python/django应用程序,除其他外,它将数据同步到各种其他服务,包括samba共享,ssh(scp)服务器,Google应用程序等.因此,它需要存储凭据才能访问这些服务.我认为将它们存储为未加密的字段将是一个坏主意,因为SQL注入攻击可以检索凭据.所以我需要在存储之前加密信用卡 - 有没有可靠的库来实现这一点?

一旦信用卡被加密,它们就需要在可用之前被解密.我的应用有两种用例:

  • 一个是交互式的 - 在这种情况下,用户将提供密码来解锁凭证.
  • 另一种是自动同步 - 这是由cron作业或类似作业启动的.我会在哪里保留密码以最大限度地降低漏洞风险?

或者我应该采取不同的方法解决这个问题?

mli*_*ner 6

我有同样的问题,过去几天一直在研究。@Rostislav提出的解决方案非常好,但是它不完整,并且过时了。

在算法层上

首先,有一个新的密码学库,适当地称为Cryptography。有很多理由使用此库而不是PyCrypto,但是吸引我的主要原因是:

  • 一个核心目标是让您无法射击自己的脚。例如,它没有像MD2 这样严重过时的哈希算法。
  • 有强大的机构支持
  • 在各种平台上进行持续集成的500,000个测试!
  • 他们的文档网站具有更好的SSL配置(接近完美的A +评分,而不是中等的B评分
  • 他们有漏洞的披露政策。

您可以阅读有关在LWN上创建新库的原因的更多信息。

其次,另一个答案建议使用SHA1作为加密密钥。SHA1的危险性越来越弱。SHA1的替代品是SHA2,最重要的是,您实际上应该对哈希值加盐并使用bcryptPBKDF2对其进行扩展。盐腌对于防止彩虹桌很重要,而拉伸则对防止蛮力逼迫很重要。

(Bcrypt的测试较少,但是被设计为使用大量内存,而PBKDF2则被设计为较慢,并且是NIST推荐的。在我的实现中,我使用PBKDF2。如果您希望更多地了解这些差异,请阅读此书。)

对于加密,如上所述,应使用具有128位密钥的CBC模式的AES-并没有改变,尽管现在它已汇总为一个称为Fernet的规范。初始化向量将在此库中自动为您生成,因此您可以放心地忽略它。

在密钥生成和存储层

其他答案很正确,建议您需要仔细考虑密钥处理,并尽可能选择OAuth之类的东西。但是假设这不可能(在我的实现中不是),您有两个用例:Cron作业和Interactive。

cron作业用例可以归结为以下事实:您需要将密钥保存在安全的地方,并使用它来运行cron作业。我还没有研究过,所以在这里我不会说。我认为有很多好的方法可以做到这一点,但是我不知道最简单的方法。

对于交互式用例,您需要做的是收集用户密码,使用该密码生成密钥,然后使用该密钥解密存储的凭据。

带回家

这是我使用密码库执行上述所有操作的方式:

from cryptography.fernet import Fernet
from cryptography.hazmat.primitives.hashes import SHA256
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend

secret = "Some secret"

# Generate a salt for use in the PBKDF2 hash
salt = base64.b64encode(os.urandom(12))  # Recommended method from cryptography.io
# Set up the hashing algo
kdf = PBKDF2HMAC(
    algorithm=SHA256(),
    length=32,
    salt=str(salt),
    iterations=100000,  # This stretches the hash against brute forcing
    backend=default_backend(),  # Typically this is OpenSSL
)
# Derive a binary hash and encode it with base 64 encoding
hashed_pwd = base64.b64encode(kdf.derive(user_pwd))

# Set up AES in CBC mode using the hash as the key
f = Fernet(hashed_pwd)
encrypted_secret = f.encrypt(secret)

# Store the safe inputs in the DB, but do NOT include a hash of the 
# user's password, as that is the key to the encryption! Only store 
# the salt, the algo and the number of iterations.
db.store(
    user='some-user', 
    secret=encrypted_secret,
    algo='pbkdf2_sha256', 
    iterations='100000', 
    salt=salt
)
Run Code Online (Sandbox Code Playgroud)

然后解密看起来像:

# Get the data back from your database
encrypted_secret, algo, iterations, salt = db.get('some-user')

# Set up the Key Derivation Formula (PBKDF2)
kdf = PBKDF2HMAC(
    algorithm=SHA256(),
    length=32,
    salt=str(salt),
    iterations=int(iterations),
    backend=default_backend(),
)
# Generate the key from the user's password
key = base64.b64encode(kdf.derive(user_pwd))

# Set up the AES encryption again, using the key
f = Fernet(key)

# Decrypt the secret!
secret = f.decrypt(encrypted_secret)
print("  Your secret is: %s" % secret)
Run Code Online (Sandbox Code Playgroud)

进攻?

假设您的数据库泄漏到了Internet。攻击者可以做什么?好吧,我们用于加密的密钥采用了用户盐腌密码的第100,000个SHA256哈希。我们将盐和加密算法存储在您的数据库中。因此,攻击者必须:

  • 尝试哈希的强力性:将salt与每个可能的密码结合起来,并将其哈希100,000次。将该哈希值用作解密密钥。攻击者仅尝试使用一个密码就必须进行100,000个哈希。这基本上是不可能的。
  • 直接尝试所有可能的哈希作为解密密钥。这基本上是不可能的。
  • 尝试使用带有预先计算的哈希值的彩虹表吗?不,不是当涉及到随机盐时。

我认为这非常可靠。

但是,还有另一件事要考虑。PBKDF2设计得很慢。它需要大量的CPU时间。这意味着,如果用户可以生成PBKDF2哈希,则您将容易受到DDOS攻击。为此做好准备。

后记

所有这些都说明了,我认为有些库可以为您完成一些工作。谷歌到处寻找django加密字段之类的东西。我不能对这些实现做任何承诺,但是也许您会学到其他人如何做到这一点的知识。


Ros*_*nko 1

首先在服务器上存储足以登录多个系统的凭据看起来像是一场噩梦。无论加密方式如何,服务器上的代码遭到破坏都会泄露所有内容。

\n\n

您应该仅存储执行任务所需的凭据(即文件同步)。对于服务器,您应该考虑使用RSync等同步服务器,对于 Google,应考虑使用OAuth等协议。这样,如果您的服务器受到威胁,只会泄漏数据,而不会泄漏对系统的访问权限。

\n\n

接下来的事情是加密这些凭证。对于密码学,我建议您使用PYCrypto

\n\n

对于您将在密码学中使用的所有随机数,通过 Crypto.Random (或其他一些强大的方法)生成它们,以确保它们足够强大。

\n\n

您不应该使用相同的密钥加密不同的凭据。我推荐的方法是这样的:

\n\n
    \n
  1. 你的服务器应该有它的主密钥M(源自 /dev/random)。将其存储在 root 拥有的文件中,并且只有 root 可读。
  2. \n
  3. 当您的服务器以 root 权限启动时,它将文件读入内存,并在为客户端提供服务之前删除它的权限。这是网络服务器和其他恶魔的正常做法。
  4. \n
  5. 当您要写入新凭证(或更新现有凭证)时,会生成一个随机块S。取前半部分并计算哈希K=H(S 1 ,M)。那将是您的加密密钥。
  6. \n
  7. 使用 CBC 模式加密您的数据。从S 2中获取初始化向量 (IV)
  8. \n
  9. 将S与加密数据一起存储。
  10. \n
\n\n

当您需要解密时,只需取出S创建K并使用相同的 IV 进行解密。

\n\n

对于哈希,我建议使用 SHA1,对于加密 \xe2\x80\x94 AES。哈希和对称密码足够快,因此使用更大的密钥不会有什么坏处。

\n\n

这个方案在某些地方有点过头了,但这也没什么坏处。

\n\n

但请再次记住,存储凭据的最佳方法不是存储凭据,并且当您必须时,请使用允许您完成任务的权限最小的凭据。

\n