Django模型中的密码字段

时间:2010-09-15 06:13:31

标签: django django-models django-admin

我正在尝试创建一个模型,我可以在其中存储其他应用程序的用户名和密码。如何在Django中设置密码字段,使其在管理员中不是纯文本?提前谢谢。

5 个答案:

答案 0 :(得分:27)

作为@mlissner suggestedauth.User模型是一个值得关注的好地方。如果您选中source code,则会看到password字段为CharField

password = models.CharField(_('password'), max_length=128, help_text=_("Use 
'[algo]$[salt]$[hexdigest]' or use the <a href=\"password/\">change password form</a>."))

User模型也有set_password方法。

def set_password(self, raw_password):
    import random
    algo = 'sha1'
    salt = get_hexdigest(algo, str(random.random()), str(random.random()))[:5]
    hsh = get_hexdigest(algo, salt, raw_password)
    self.password = '%s$%s$%s' % (algo, salt, hsh)

您可以从此方法中获取有关创建密码并保存密码的一些线索。

答案 1 :(得分:5)

我认为您无法解密以与普通django用户密码类似的方式存储的加密密码。部分安全性是它们不可拆卸。

答案 2 :(得分:4)

不幸的是,这个问题没有一个简单的答案,因为它取决于您尝试进行身份验证的应用程序,而且还取决于您希望密码字段的安全程度。

如果您的Django应用程序将使用密码对另一个需要发送明文密码的应用程序进行身份验证,那么您的选项是:

  • 将密码以纯文本形式存储在Django模型中(您的问题意味着您不想这样做)
  • 在用户解锁其他应用程序的存储密码之前,先从用户处获取主密码
  • 对模型中的密码进行模糊处理,以便任何拥有原始数据存储权限的人都可以访问该密码,但这对于人类偶然的观众来说并不明显。

如果您使用的是Django的内置用户模型,则可以使用Django用户密码作为主密码。这意味着您需要将该主密码保留在内存中,这可能会使您难以完成某些操作,例如重新启动服务器或运行负载均衡的冗余服务器。

存储密码的替代方法

幸运的是,许多现代应用程序使用基于密钥而非基于密码的访问令牌系统以另一种方式支持这一点。引导用户完成在两个应用程序之间建立链接的过程,并且在后台,应用程序生成密钥以永久地或以定义的过期日期相互验证。

例如,Facebook支持这种模式,并且他们有关于它如何运作的大量文档:

Facebook Developers: Access Tokens and Types

使用[OAuth 2.0](http://tools.ietf.org/html/draft-ietf-oauth-v2-12)设法与Facebook建立链接后,您可能会发现更容易向其他人添加链接使用相同协议的应用程序。

答案 3 :(得分:3)

你最好的选择(我知道)是深入研究django代码中的代码,看看它是如何完成的。我记得,他们生成一个salted哈希,以便纯文本值永远不会存储在任何地方,而是哈希和盐。

如果你进入django安装,然后四处寻找像哈希和盐这样的词,你应该很快找到它。对于模糊的回答感到抱歉,但也许它会让你走上正确的道路。

答案 4 :(得分:2)

如果您需要可逆密码字段,可以使用以下内容:

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())]

加密部分基于https://djangosnippets.org/snippets/1330/上的代码段,我只是将其转换为字段模型,添加了utf-8支持并添加了前缀作为Django's silly use of to_python()的解决方法