Django模型中的密码字段
我正在尝试创建一个模型,在这个模型中,我可以存储其他应用程序的用户名和密码。如何在Django中设置密码字段,使其在admin中不是纯文本?提前感谢。我知道,你最好的选择是深入研究django代码中的代码,看看它是如何实现的。我记得,它们生成了一个salt散列,因此纯文本值永远不会存储在任何地方,而是散列和salt存储在任何地方 如果您进入django安装程序,四处寻找诸如hash和salt之类的单词,您应该会很快找到它。很抱歉回答得含糊不清,但也许这会让你走上正确的道路。作为@mlissner,Django模型中的密码字段,django,django-models,django-admin,Django,Django Models,Django Admin,我正在尝试创建一个模型,在这个模型中,我可以存储其他应用程序的用户名和密码。如何在Django中设置密码字段,使其在admin中不是纯文本?提前感谢。我知道,你最好的选择是深入研究django代码中的代码,看看它是如何实现的。我记得,它们生成了一个salt散列,因此纯文本值永远不会存储在任何地方,而是散列和salt存储在任何地方 如果您进入django安装程序,四处寻找诸如hash和salt之类的单词,您应该会很快找到它。很抱歉回答得含糊不清,但也许这会让你走上正确的道路。作为@mlissner
auth.User
模型是一个好地方。如果选中,您将看到密码
字段是字符字段
password = models.CharField(_('password'), max_length=128, help_text=_("Use
'[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))
您可以从这个方法中获得一些关于创建密码和保存密码的线索 我认为您永远无法对以类似于普通django用户密码的方式存储的加密密码进行反散列。安全性的一部分在于它们是不可解析的。不幸的是,这个问题没有一个简单的答案,因为它取决于您试图对其进行身份验证的应用程序,也取决于您希望密码字段的安全程度 如果您的Django应用程序将使用密码对另一个需要发送明文密码的应用程序进行身份验证,则您可以选择:
- 在Django模型中以纯文本形式存储密码(您的问题暗示您不想这样做)
- 在用户为其他应用程序解锁存储的密码之前,从用户处捕获主密码
- 混淆模型中的密码,以便任何具有原始数据存储权限的人都可以访问该密码,但对普通观众来说并不明显
一旦您使用[OAuth 2.0]成功链接到Facebook(http://tools.ietf.org/html/draft-ietf-oauth-v2- 12) 您可能会发现使用相同协议添加到其他应用程序的链接更容易。如果需要可逆密码字段,您可以使用如下内容:
from django.db import models
from django.core.exceptions import ValidationError
from django.conf import settings
from os import urandom
from base64 import b64encode, b64decode
from Crypto.Cipher import ARC4
from django import forms
PREFIX = u'\u2620'
class EncryptedCharField(models.CharField):
__metaclass__ = models.SubfieldBase
SALT_SIZE = 8
def __init__(self, *args, **kwargs):
self.widget = forms.TextInput
super(EncryptedCharField, self).__init__(*args, **kwargs)
def get_internal_type(self):
return 'TextField'
def to_python(self, value):
if not value:
return None
if isinstance(value, basestring):
if value.startswith(PREFIX):
return self.decrypt(value)
else:
return value
else:
raise ValidationError(u'Failed to encrypt %s.' % value)
def get_db_prep_value(self, value, connection, prepared=False):
return self.encrypt(value)
def value_to_string(self, instance):
encriptado = getattr(instance, self.name)
return self.decrypt(encriptado) if encriptado else None
@staticmethod
def encrypt(plaintext):
plaintext = unicode(plaintext)
salt = urandom(EncryptedCharField.SALT_SIZE)
arc4 = ARC4.new(salt + settings.SECRET_KEY)
plaintext = u"%3d%s%s" % (len(plaintext), plaintext, b64encode(urandom(256-len(plaintext))))
return PREFIX + u"%s$%s" % (b64encode(salt), b64encode(arc4.encrypt(plaintext.encode('utf-8-sig'))))
@staticmethod
def decrypt(ciphertext):
salt, ciphertext = map(b64decode, ciphertext[1:].split('$'))
arc4 = ARC4.new(salt + settings.SECRET_KEY)
plaintext = arc4.decrypt(ciphertext).decode('utf-8-sig')
return plaintext[3:3+int(plaintext[:3].strip())]
加密部分基于上的代码片段,我刚刚将其转换为一个字段模型,添加了utf-8支持,并添加了前缀作为的解决方案,谢谢大家的回答。我支持姆利斯纳和马诺伊的建议,不过要特别注意rebus,因为他的方法适用于简单的应用程序,您只需要简单的功能而不需要太多的安全性。这可能会满足您的需要。Ping-你实现了这个吗?我正在考虑实现一个类似的东西,但我不确定从哪里开始你能给我指出正确的方向吗?杰克-我有,但我做了一点不同。我像Manoj在回答中所说的那样使用set_密码,从那里我可以通过拆分algo/salt/hash字符串来检查用户登录时输入的密码,只留下hash进行比较,或者使用静态salt对它们进行比较。因此,如果我要实现类似的方法,当我需要使用密码通过Facebook之类的API进行身份验证时,我该如何对密码进行反散列呢?正如Marc在下面回答的,你不能。但是您可以使用相同的algo和salt对用户提供的密码进行哈希运算,并检查该值是否等于您存储在数据库中的值。嗨,源代码链接指向404,你能提供一个更正的链接吗?自从这个答案发布以来,Django已经改变了很多-允许覆盖和修改用户模型-但是要点仍然是正确的。新的
AbstractBaseUser
密码设置和检查的重起重部件就在这里。同样,虽然这个答案的散文一般都是正确的,但代码已经过时了。答案可能需要仔细重写。要使用上述方法,您可能需要pip-install-pycrypto
而不是pip-install-Crypto
。此外,pypi上现在有一个包含许多字段的包,名为django-encrypted-fields
(对于python 3,django-encrypted-fields-python3
)。散列的整个要点是不可逆的!任何人都不能访问原始密码。是的,这是真的,如果你能用散列来解决问题,你一定要用它。但是,有时您可能需要检索密码:例如,假设您的程序需要登录到第三方服务器,并且访问该第三方服务器的密码在您的数据库中。
from django.db import models
from django.core.exceptions import ValidationError
from django.conf import settings
from os import urandom
from base64 import b64encode, b64decode
from Crypto.Cipher import ARC4
from django import forms
PREFIX = u'\u2620'
class EncryptedCharField(models.CharField):
__metaclass__ = models.SubfieldBase
SALT_SIZE = 8
def __init__(self, *args, **kwargs):
self.widget = forms.TextInput
super(EncryptedCharField, self).__init__(*args, **kwargs)
def get_internal_type(self):
return 'TextField'
def to_python(self, value):
if not value:
return None
if isinstance(value, basestring):
if value.startswith(PREFIX):
return self.decrypt(value)
else:
return value
else:
raise ValidationError(u'Failed to encrypt %s.' % value)
def get_db_prep_value(self, value, connection, prepared=False):
return self.encrypt(value)
def value_to_string(self, instance):
encriptado = getattr(instance, self.name)
return self.decrypt(encriptado) if encriptado else None
@staticmethod
def encrypt(plaintext):
plaintext = unicode(plaintext)
salt = urandom(EncryptedCharField.SALT_SIZE)
arc4 = ARC4.new(salt + settings.SECRET_KEY)
plaintext = u"%3d%s%s" % (len(plaintext), plaintext, b64encode(urandom(256-len(plaintext))))
return PREFIX + u"%s$%s" % (b64encode(salt), b64encode(arc4.encrypt(plaintext.encode('utf-8-sig'))))
@staticmethod
def decrypt(ciphertext):
salt, ciphertext = map(b64decode, ciphertext[1:].split('$'))
arc4 = ARC4.new(salt + settings.SECRET_KEY)
plaintext = arc4.decrypt(ciphertext).decode('utf-8-sig')
return plaintext[3:3+int(plaintext[:3].strip())]