我国政府已限制HTTPS速度,以阻止伊朗境外安全网络服务的访问。现在我的客户很难登录他们的帐户。我知道当前帐户密码是使用pbkdf2_sha256
算法加密和加密的,并且有一些javascript / jQuery库可以消化sha256哈希值。
我的问题:是否有任何无痛的方式(不需要重写/更改原始django.contrib.auth
)以使用AJAX请求发送的sha256哈希作为登录密码?
更新:我打算在伊朗境内托管我的网站(费用是政府的5倍,当然由政府控制),但至少HTTPS协议不受限制。无论如何,通过HTTP通道以纯文本或消化方式发送密码是不安全的。虽然作为Web开发人员(而不是网络专家),我认为他们可以随时收集/嗅探哈希/会话ID / cookie,解决问题需要复杂的知识和努力,毕竟我的网站不需要那种安全级别。我们生活在不同的星球。
答案 0 :(得分: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
将指向代理类。
有关详细信息,请参阅documentation。
对于Django 1.5 + there is also an option,用自定义用户模型替换默认用户模型,但为了保留现有用户,您必须以某种方式迁移它们,请参阅this question。
实际上,您将无法保持用户密码不变。
默认情况下,Django以下列格式存储密码:
算法$ $迭代盐$散列
这意味着:
您不能只从原件的哈希重新生成密码,因为您没有原件。
在不知道salt的情况下,您也无法在客户端生成相同的哈希。您可以将它传递给客户端,但盐应该是一个秘密,因此通过未加密的通道进行操作是不明智的。
我看到的最简单的解决方案是保持当前的Django行为,如评论中Tadeck所建议的那样,将哈希添加到客户端并强制用户更改其密码。
嗯,这不是一个真正的解决方案,因为攻击者可以拦截已消化的密码并直接使用它们,但是你提到了你的问题更新。由于您不太关心安全性,因此您还可以在JavaScript中查看public key encryption。
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})
攻击者仍然可以拦截访问令牌,但它比拦截密码哈希要好一些。