From a11d7d011cf4049e55f98e7ffc9d6448a35abd30 Mon Sep 17 00:00:00 2001 From: "Pavel \"LEdoian\" Turinsky" Date: Tue, 18 May 2021 21:45:20 +0200 Subject: [PATCH] =?UTF-8?q?Implementov=C3=A1na=20validace=20token=C5=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implementoval jsem to od stolu, není to vyzkoušené. --- seminar/views/doregistrace.py | 46 +++++++++++++++++++++++++---------- 1 file changed, 33 insertions(+), 13 deletions(-) diff --git a/seminar/views/doregistrace.py b/seminar/views/doregistrace.py index 6e1472fb..39fbff74 100644 --- a/seminar/views/doregistrace.py +++ b/seminar/views/doregistrace.py @@ -10,6 +10,7 @@ Importovat prosím jen ty dva Views, ve výjimečných případech invite, nic d #TODO: Logování (tohle logovat chce skoro určitě) #TODO: Omezení počtu pokusů (per token -- města / údaje se bruteforcit dají, na rozdíl od tokenů) +from datetime import datetime from enum import Enum from django import forms from django.contrib.auth.models import User, Permission @@ -38,9 +39,11 @@ class DodatecnaRegistraceUzivateleView(TemplateResponseMixin, View): status_code=400, ) + osoba_id, tok_timestamp = tok_data # Token prostě vypadá takhle, je to zafixované. Pokud tak nevypadá, je něco moc špatně. + # Zkontrolovat, že to není moc staré poslání now = datetime.now() - token_generation = datetime.fromisoformat(tok_data['timestamp']) + token_generation = datetime.fromisoformat(tok_timestamp) delta = now - token_generation if delta >= timedelta(weeks=10): return render_to_response( @@ -52,7 +55,6 @@ class DodatecnaRegistraceUzivateleView(TemplateResponseMixin, View): # Najít osobu - osoba_id = int(tok_data['osoba']) # Pokud tam není, tak jsme vygenerovali špatný token my (byl validní). osoba = m.Osoba.objects.get(id=osoba_id) if osoba.user is not None: return render_to_response( @@ -62,10 +64,7 @@ class DodatecnaRegistraceUzivateleView(TemplateResponseMixin, View): status_code=400, ) # Vrátit view s formulářem a formulářovým tokenem - form_token = gen_token(UseCase.form, data={ - 'osoba': str(osoba_id), - 'timestamp': datetime.now().isoformat(), - }) + form_token = gen_token(UseCase.form, osoba=osoba_id, timestamp=datetime.now()) return render_to_response( context={ 'form': self.form(initial={ @@ -151,15 +150,36 @@ class UseCase(Enum): form = 'form' # Token kóduje všechno v sobě -def gen_token(usecase: _UseCase, data: dict[str, str]) -> str: - ... - -def verify_token(usecase: _UseCase, token: str) -> Optional[dict[str, str]]: +# Tokenové metody zároveň řeší konzistentní zakódování dat do tokenu, takže už to nemusí řešit nikdo jiný. +HMAC_FUNCTION='sha-256' +def gen_token(usecase: _UseCase, *, osoba_id: int, timestamp: datetime) -> str: + strmsg = '@'.join([usecase.value, str(osoba_id), timestamp.isoformat()]) + msg = bytes(strmsg, 'utf-8') + key = bytes(SECRET_KEY, 'utf-8') + mac = hmac.mac(key, msg, HMAC_FUNCTION).hexdigest() + return mac+'@'+msg + +def verify_token(usecase: _UseCase, token: str) -> Optional[Tuple[int, datetime]]: """ - Vrací slovník dat, pokud je token validní, jinak None. + Vrací dvojici dat, pokud je token validní, jinak None. Inspirováno OSMO, je díky tomu jednoduché zároveň předat dekódovaná data a výsledek ověření. """ - ... - + try: + tok_mac, uc, tok_osoba, tok_ts = token.split('@') + except ValueError: + # Nepodařilo se rozbít na právě čtyři části, takže je token špatně. + return None + strmsg = '@'.join([usecase.value, tok_osoba, tok_ts]) + msg = bytes(strmsg, 'utf-8') + key = bytes(SECRET_KEY, 'utf-8') + valid_mac = hmac.mac(key, msg, HMAC_FUNCTION).hexdigest() + + if hmac.compare_digest(tok_mac, valid_mac): + # HMAC sedí, takže data jsou v pořádku + osoba_id = int(tok_osoba) + ts = datetime.fromisoformat(tok_ts) + return (osoba_id, ts) + else: + return None