From 2264b0886f34e66995c4a480ab3a9d784e552e34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Th=C3=A9ophile=20Bastian?= Date: Mon, 16 Oct 2017 20:02:36 +0200 Subject: [PATCH] Change API protocol to fix json normalization A dictionnary's order is not deterministic (eg. when switching python versions), thus the previous protocol would often fail to authenticate a legitimate request. --- api/client/apiclient.py | 5 +++-- api/views.py | 20 ++++++++++++++------ 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/api/client/apiclient.py b/api/client/apiclient.py index fa8825e..e6016db 100644 --- a/api/client/apiclient.py +++ b/api/client/apiclient.py @@ -33,7 +33,8 @@ def sendReq(url): mac = hmac.new(key.encode('utf-8'), msg=str(int(time)).encode('utf-8'), digestmod=hashlib.sha256) - mac.update(json.dumps(payload).encode('utf-8')) + payload_enc = json.dumps(payload) + mac.update(payload_enc.encode('utf-8')) auth = { 'keyId': keyId, @@ -43,7 +44,7 @@ def sendReq(url): return { 'auth': auth, - 'req': payload, + 'req': payload_enc, } def decorator(fct): diff --git a/api/views.py b/api/views.py index 200ff06..f015373 100644 --- a/api/views.py +++ b/api/views.py @@ -20,8 +20,7 @@ def authentify(data, payload): except models.ApiKey.DoesNotExist: return response.HttpResponseForbidden('Bad authentication') - normPayload = json.dumps(payload) - if not key.isCorrect(data['timestamp'], data['hmac'], normPayload): + if not key.isCorrect(data['timestamp'], data['hmac'], payload): return response.HttpResponseForbidden('Bad authentication') @@ -30,22 +29,31 @@ def apiView(required=[]): @csrf_exempt def wrap(request, *args, **kwargs): try: - data = json.loads(request.body) - except json.decoder.JSONDecoreError: + data = json.loads(request.body.decode('utf-8')) + except TypeError: + return response.HttpResponseBadRequest("Bad packet format") + except json.decoder.JSONDecodeError: return response.HttpResponseBadRequest("Bad json") try: authData = data['auth'] - reqData = data['req'] + reqDataOrig = data['req'] except KeyError: return response.HttpResponseBadRequest("Bad request format") + try: + reqData = json.loads(reqDataOrig) + except TypeError: + return response.HttpResponseBadRequest("Bad packet format") + except json.decoder.JSONDecodeError: + return response.HttpResponseBadRequest("Bad inner json") + for field in required: if field not in reqData: return response.HttpResponseBadRequest( "Missing field {}".format(field)) - authVal = authentify(authData, reqData) + authVal = authentify(authData, reqDataOrig) if authVal is not None: return authVal