您可以编写自己的身份验证后端以使用原始密码:
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):
self.password = raw_password
def check_password(self, 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_required
装饰器也是如此,
request.user
将指向代理类。
有关详细信息,请参见
文档。
对于Django 1.5+,还有一个选项可以使用自定义模型替换默认的User模型,但要保留现有用户,您需要以某种方式对其进行迁移,请参见
此问题。
这里也有相应的说明。
实际上,您无法保持用户密码不变。默认情况下,Django以以下格式存储密码: 算法$迭代次数$盐$哈希值。这意味着: 您不能仅从原始哈希重新生成密码,因为您没有原始密码。您也无法在客户端生成相同的哈希值,而不知道盐。您可以将其传递到客户端,但是盐应该是一个秘密,所以通过未加密的通道进行传递是不明智的。我看到的最简单的解决方案是保持当前的Django行为,如评论中
Tadeck所建议的,添加客户端哈希并强制用户更改密码。好吧,这不是真正的解决方案,因为攻击者可以拦截摘要密码并直接使用它们,但是您在问题更新中提到了它。由于您并不太关心安全性,因此您还可以在JavaScript中查看
公钥加密。
Tadeck提出的另一个解决方案是使用类似OAuth的服务,可能看起来像这样:
def index(request):
access_token = request.REQUEST.get('token', None)
if not access_token:
return redirect('login')
user = authenticate(access_token)
if not user:
return redirect('login')
return render(...)
def auth(request):
''' This ajax-view has to be encrypted with SSL.'''
user = authenticate(request.POST['username'], request.POST['password'])
if user is None:
return json.dumps({'error': '...'})
token = UserToken(user=user, value=generate_token())
token.save()
return json.dumps({'token': token.value})
攻击者仍然可以截取访问令牌,但相对于截取密码哈希来说要好一些。
except
放在user = User.objects.get(...)
的下面。 - Oscar Mederos