6

我国政府已限制 HTTPS 速度,以阻止访问伊朗以外的安全 Web 服务。现在我的客户因为登录他们的帐户而感到痛苦。我知道当前帐户密码是使用pbkdf2_sha256算法加密和加盐的,并且有一些 javascript/jQuery 库可以消化 sha256 哈希。

我的问题:是否有任何轻松的方式(不需要重写/更改原始内容django.contrib.auth)使用 AJAX 请求发送的 sha256 哈希作为登录密码?

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

4

1 回答 1

3

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

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',
)

现在,当您authenticate在视图中使用用户时,您将获得RawPasswordUser实例。同样适用于login_required装饰器,request.user将指向代理类。

有关详细信息,请参阅文档

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


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

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

算法$迭代$盐$哈希

意思是:

  • 您不能只从原始哈希中重新生成密码,因为您没有原始密码。

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

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

好吧,这不是一个真正的解决方案,因为攻击者可以拦截消化的密码并直接使用它们,但你提到它是你的问题更新。由于您不太关心安全性,因此您还可以检查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})

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

于 2013-05-02T15:24:39.237 回答