web-dev-qa-db-ja.com

Djangoモデルのパスワードフィールド

他のアプリケーションのユーザー名とパスワードを保存できるモデルを作成しようとしています。 Djangoでパスワードフィールドを設定して、adminでプレーンテキストにならないようにするにはどうすればよいですか?よろしくお願いします。

26
Ruben Quinones

@mlissnerとして 推奨 the auth.Userモデルはよく見る場所です。 ソースコード を確認すると、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)

この方法から、パスワードの作成と保存に関する手掛かりをいくつか入手できます。

27
Manoj Govindan

通常のDjangoユーザーパスワードと同様の方法で保存された暗号化されたパスワードをハッシュ解除できるようになることはないと思います。セキュリティの一部は、非ハッシュ化可能。

5
Marc

残念ながら、この質問に対する簡単な答えはありません。これは、認証しようとしているアプリケーションに依存し、また、パスワードフィールドの安全性にも依存するためです。

Django=アプリケーションがパスワードを使用して、プレーンテキストのパスワードを送信する必要がある別のアプリケーションに対して認証を行う場合、オプションは次のとおりです。

  • Djangoモデルにパスワードをプレーンテキストで保存します(質問は、これを実行したくないことを意味します)
  • 他のアプリケーション用に保存されているパスワードのロックを解除する前に、ユーザーからマスターパスワードを取得します
  • 生のデータストア権限を持つ誰でもアクセスできるように、モデルのパスワードを難読化しますが、人間のカジュアルな閲覧者にはわかりません

Djangoの組み込みユーザーモデルを使用している場合は、マスターパスワードとしてDjangoユーザーパスワードを使用できます。これは、そのマスターパスワードをメモリに保持する必要があるため、一部の操作が困難になる可能性がありますサーバーの再起動や負荷分散された冗長サーバーの実行など。

パスワードを保存する代わり

幸いにも、多くの最新のアプリケーションは、パスワードベースではなくキーベースのアクセストークンシステムを使用して別の方法でこれをサポートしています。ユーザーは2つのアプリケーション間のリンクを設定するプロセスを案内され、舞台裏で、アプリケーションは永続的にまたは定義された有効期限で相互に認証するためのキーを生成します。

たとえば、Facebookはこのモデルをサポートしており、その仕組みに関する広範なドキュメントを持っています。

Facebook開発者:アクセストークンとタイプ

[OAuth 2.0](http://tools.ietf.org/html/draft-ietf-oauth-v2- 12)を使用してFacebookとリンクすることができたら、おそらくそれを使用して他のアプリケーションへのリンクを追加する方が簡単でしょう。同じプロトコル。

4
jwal

あなたの最善の策(私は知っています)は、Djangoコードのコードを詳しく調べて、それがどのように行われるかを確認することです。覚えているように、単純なテキスト値はどこにも保存されませんが、ハッシュとソルトは保存されます。

Djangoインストールに移動して、ハッシュやソルトなどの単語を探すと、すぐに見つけられるはずです。あいまいな答えで申し訳ありませんが、おそらく正しいパスに設定されるでしょう。 。

3
mlissner

元に戻せるパスワードフィールドが必要な場合は、次のようなものを使用できます。

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サポートを追加し、回避策としてプレフィックスを追加しましたfor Djangoのto_python()のばかげた使用

2
Haroldo_OK