Change login flow to better verify all relevant fields.

This commit is contained in:
Tom Hacohen 2020-05-14 15:42:42 +03:00
parent 32a8b9c90d
commit 93a0e41f03
2 changed files with 46 additions and 36 deletions

View File

@ -244,19 +244,21 @@ class AuthenticationLoginChallengeSerializer(serializers.Serializer):
raise NotImplementedError() raise NotImplementedError()
class AuthenticationLoginSerializer(AuthenticationLoginChallengeSerializer): class AuthenticationLoginSerializer(serializers.Serializer):
challenge = BinaryBase64Field() response = BinaryBase64Field()
host = serializers.CharField()
signature = BinaryBase64Field() signature = BinaryBase64Field()
def validate(self, data): def create(self, validated_data):
host = self.context.get('host', None) raise NotImplementedError()
if data['host'] != host:
raise serializers.ValidationError( def update(self, instance, validated_data):
'Found wrong host name. Got: "{}" expected: "{}"'.format(data['host'], host)) raise NotImplementedError()
return super().validate(data)
class AuthenticationLoginInnerSerializer(AuthenticationLoginChallengeSerializer):
challenge = BinaryBase64Field()
host = serializers.CharField()
def create(self, validated_data): def create(self, validated_data):
raise NotImplementedError() raise NotImplementedError()

View File

@ -40,6 +40,7 @@ from .serializers import (
AuthenticationSignupSerializer, AuthenticationSignupSerializer,
AuthenticationLoginChallengeSerializer, AuthenticationLoginChallengeSerializer,
AuthenticationLoginSerializer, AuthenticationLoginSerializer,
AuthenticationLoginInnerSerializer,
CollectionSerializer, CollectionSerializer,
CollectionItemSerializer, CollectionItemSerializer,
CollectionItemRevisionSerializer, CollectionItemRevisionSerializer,
@ -368,12 +369,17 @@ class AuthenticationViewSet(viewsets.ViewSet):
def login(self, request): def login(self, request):
from datetime import datetime from datetime import datetime
serializer = AuthenticationLoginSerializer( outer_serializer = AuthenticationLoginSerializer(data=request.data)
data=request.data, context={'host': request.get_host()}) if outer_serializer.is_valid():
response_raw = outer_serializer.validated_data['response']
response = json.loads(response_raw.decode())
signature = outer_serializer.validated_data['signature']
serializer = AuthenticationLoginInnerSerializer(data=response, context={'host': request.get_host()})
if serializer.is_valid(): if serializer.is_valid():
user = self.get_login_user(serializer) user = self.get_login_user(serializer)
host = serializer.validated_data['host']
challenge = serializer.validated_data['challenge'] challenge = serializer.validated_data['challenge']
signature = serializer.validated_data['signature']
salt = user.userinfo.salt salt = user.userinfo.salt
enc_key = self.get_encryption_key(salt) enc_key = self.get_encryption_key(salt)
@ -387,11 +393,13 @@ class AuthenticationViewSet(viewsets.ViewSet):
elif challenge_data['userId'] != user.id: elif challenge_data['userId'] != user.id:
content = {'code': 'wrong_user', 'detail': 'This challenge is for the wrong user'} content = {'code': 'wrong_user', 'detail': 'This challenge is for the wrong user'}
return Response(content, status=status.HTTP_400_BAD_REQUEST) return Response(content, status=status.HTTP_400_BAD_REQUEST)
elif host != request.get_host():
detail = 'Found wrong host name. Got: "{}" expected: "{}"'.format(host, request.get_host())
content = {'code': 'wrong_host', 'detail': detail}
return Response(content, status=status.HTTP_400_BAD_REQUEST)
host_hash = nacl.hash.blake2b(
serializer.validated_data['host'].encode(), encoder=nacl.encoding.RawEncoder)
verify_key = nacl.signing.VerifyKey(user.userinfo.pubkey, encoder=nacl.encoding.RawEncoder) verify_key = nacl.signing.VerifyKey(user.userinfo.pubkey, encoder=nacl.encoding.RawEncoder)
verify_key.verify(challenge + host_hash, signature) verify_key.verify(response_raw, signature)
data = { data = {
'token': Token.objects.get_or_create(user=user)[0].key, 'token': Token.objects.get_or_create(user=user)[0].key,