Django 使用sha256哈希而不是纯文本密码

Django 使用sha256哈希而不是纯文本密码,django,Django,我国政府限制了HTTPS的速度,以阻止访问伊朗境外的安全web服务。现在我的客户因为登录他们的帐户而感到痛苦。我知道活期账户密码是使用pbkdf2_sha256算法加密和加密的,还有一些javascript/jQuery lib来消化sha256哈希 我的问题是:有没有任何无痛的方法(不需要重写/更改原始的django.contrib.auth)来使用AJAX请求发送的sha256哈希作为登录密码 更新:我计划在伊朗境内托管我的网站(费用是伊朗的5倍,当然由政府控制),但至少HTTPS协议不受限

我国政府限制了HTTPS的速度,以阻止访问伊朗境外的安全web服务。现在我的客户因为登录他们的帐户而感到痛苦。我知道活期账户密码是使用
pbkdf2_sha256
算法加密和加密的,还有一些javascript/jQuery lib来消化sha256哈希

我的问题是:有没有任何无痛的方法(不需要重写/更改原始的
django.contrib.auth
)来使用AJAX请求发送的sha256哈希作为登录密码


更新:我计划在伊朗境内托管我的网站(费用是伊朗的5倍,当然由政府控制),但至少HTTPS协议不受限制。通过HTTP通道以明文或摘要方式发送密码无论如何都是不安全的。作为一名web开发人员(而不是网络专家),我认为他们可以随时收集/嗅探哈希/会话ID/cookies,解决问题需要复杂的知识和努力,毕竟我的网站不需要那种级别的安全性。我们生活在不同的星球上。

您可以编写自己的身份验证后端以使用原始密码:

from django.contrib.auth import backends
from django.contrib.auth.models import User

class RawPasswordUser(User):
    class Meta:
        proxy = True

    def set_password(self, raw_password):
        # default implementation made a hash from raw_password,
        # we don't want this
        self.password = raw_password

    def check_password(self, raw_password):
        # same here, don't make hash out of raw_password
        return self.password == raw_password

class ModelBackend(backends.ModelBackend):
    def authenticate(self, username=None, password=None):
        try:
            user = RawPasswordUser.objects.get(username=username)
            if user.check_password(password):
                return user
        except RawPasswordUser.DoesNotExist:
            return None

    def get_user(self, user_id):
        try:
            return RawPasswordUser.objects.get(pk=user_id)
        except RawPasswordUser.DoesNotExist:
            return None
在设置文件中:

AUTHENTICATION_BACKENDS = (
    # ModelBackend from project_root/auth/backends.py
    'auth.backends.ModelBackend',
)
现在,当您对视图中的用户进行
身份验证时,您将获得
RawPasswordUser
实例。这同样适用于
login\u required
decorator,
request。用户将指向代理类

有关详细信息,请参阅

对于Django 1.5+,要用自定义用户模型替换默认用户模型,但要保留现有用户,您必须以某种方式迁移它们,请参阅


实际上,您将无法保持用户密码不变

默认情况下,Django以以下格式存储密码:

算法$iterations$salt$hash

这意味着:

  • 你不能仅仅从原始密码的散列中重新生成密码,因为你没有原始密码

  • 在不知道salt的情况下,您也无法在客户端生成相同的哈希。您可以将其传递给客户端,但salt应该是一个秘密,所以通过未加密的通道进行传递是不明智的

我看到的最简单的解决方案是保持当前的Django行为,正如在评论中所建议的那样,在客户端添加哈希,并强制用户更改密码

好吧,这不是一个真正的解决方案,因为攻击者可以截获摘要密码并直接使用它们,但你提到了你的问题更新。因为您不太关心安全性,所以也可以使用JavaScript进行签出


Tadeck提出的另一个解决方案是使用类似OAuth的服务,它可能看起来有点像这样:

def index(request):
    access_token = request.REQUEST.get('token', None)
    if not access_token:
        return redirect('login')

    # Custom authentication backend that accepts a token
    # and searches for a user with that token in database.
    user = authenticate(access_token)
    if not user:
        return redirect('login')

    return render(...)

def auth(request):
    ''' This ajax-view has to be encrypted with SSL.'''
    # Normal Django authentication.
    user = authenticate(request.POST['username'], request.POST['password'])

    # Authentication failed
    if user is None:
        return json.dumps({'error': '...'})

    # generate, save and return token in json response
    token = UserToken(user=user, value=generate_token())
    # token.expires_at = datetime.now() + timedelta(days=1)
    token.save()

    return json.dumps({'token': token.value})

受攻击的攻击者仍然可以拦截访问令牌,但这比拦截密码哈希要好一点。

欢迎使用堆栈溢出。请,谢谢。对不起,我要说的话。在过去的33个小时里,我一直没有睡觉,并且因为一个不是我的错误导致的问题而头痛不已。我会将
除了
之外的
放在
user=user.objects.get(…)
的正下方。是否需要更改当前的后端记录?我的意思是,当使用这个新的后端时,当前用户应该更改他们的密码吗?我可以写一些脚本来转换它们吗?密码仍然是散列,您的客户端发送相同的散列,然后您不需要转换任何内容。如果哈希值不同,您可以在
python manage.py shell
中进行转换,导入
django.contrib.auth.models.User
并根据需要进行修改。这是解决类似问题的一种不好的方法:您不需要这样做就降低了安全性(据我所知,OP的政府不需要服务器端漏洞;这将是愚蠢的)。OP可能需要的实际上是双重哈希。后端可以保持不变,但前端(在注册和验证期间;加上密码重置)会一致地哈希密码(重要的是要确保相同的密码将提供相同的输出,最好是加一些盐)然后这个散列将被视为密码。但是这仍然不理想。更好的方法是使用非常轻的页面版本,使用类似于OAuth的服务进行身份验证:OP的SSL站点将充当OAuth提供者,而非SSL站点将充当OAuth使用者,这使得过程足够安全,但允许快速访问通过政府渠道监控的不安全访问。此外,您可以定期重定向到SSL版本以检查身份验证有效性(如果您以后有办法使其无效)。这可以给您的消费者带来额外的安全感,确保他们的密码是安全的,而用户体验不会受到影响。