Skip to content

Commit

Permalink
Merge pull request #17 from tomaspalma/fix/exchange-confirmation-repl…
Browse files Browse the repository at this point in the history
…ay-attack

Prevent confirmation replay attack
  • Loading branch information
tomaspalma committed Apr 5, 2024
2 parents 8d933ae + 9f1116e commit 6929893
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 18 deletions.
3 changes: 3 additions & 0 deletions django/.env.dev
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ MYSQL_PASSWORD=root
MYSQL_USER=root
MYSQL_HOST=db
MYSQL_PORT=3306

REDIS_PORT=6379
REDIS_HOST=tts-be-redis_service-1
9 changes: 9 additions & 0 deletions django/tts_be/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,5 +148,14 @@
]
}

CACHES = {
'default': {
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
'LOCATION': f"redis://{CONFIG['REDIS_HOST']}:{CONFIG['REDIS_PORT']}//"
}
}

CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True

VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS = 3600 * 24
55 changes: 37 additions & 18 deletions django/university/views.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from django.http.response import HttpResponse
from tts_be.settings import JWT_KEY
from tts_be.settings import JWT_KEY, VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS
from university.exchange.utils import course_unit_name, curr_semester_weeks, get_student_schedule_url, build_student_schedule_dict, exchange_overlap, build_student_schedule_dicts
from university.exchange.utils import ExchangeStatus, build_new_schedules, check_for_overlaps
from university.models import Faculty
Expand All @@ -23,7 +23,9 @@
import jwt
import json
import datetime
import time
from django.utils import timezone
from django.core.cache import cache
# Create your views here.


Expand Down Expand Up @@ -303,8 +305,6 @@ def submit_direct_exchange(request):
# user_schedule_offset = DirectExchangeParticipants.objects.filter("""direct_exchange__accepted=True,""" participant=request.session["username"], accepted=True)
user_schedule_offset = DirectExchangeParticipants.objects.filter(participant=request.session["username"])

#check_for_overlaps()

student_schedules[request.session["username"]] = build_student_schedule_dict(json.loads(curr_student_schedule.content)["horario"])

(status, trailing) = build_student_schedule_dicts(student_schedules, exchanges, semana_ini, semana_fim, request.COOKIES)
Expand All @@ -323,32 +323,51 @@ def submit_direct_exchange(request):
return JsonResponse({"error": "classes-overlap"}, status=400, safe=False)

exchange_model.save()


tokens_to_generate = {}
for inserted_exchange in inserted_exchanges:
participant = inserted_exchange.participant;
if not(participant in tokens_to_generate):
token = jwt.encode({"username": participant, "exchange_id": exchange_model.id, "exp": (datetime.datetime.now() + datetime.timedelta(seconds=VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS)).timestamp()}, JWT_KEY, algorithm="HS256")
tokens_to_generate[participant] = token
inserted_exchange.save()

# 1. Create token
token = jwt.encode({"username": request.session["username"], "exchange_id": exchange_model.id, "exp": (datetime.datetime.now() + datetime.timedelta(hours=24)).timestamp()}, JWT_KEY, algorithm="HS256")
print(token)

# 2. Send confirmation email

return JsonResponse({"success": True}, safe=False)

@api_view(["POST"])
def verify_direct_exchange(request, token):
exchange_info = jwt.decode(token, JWT_KEY, algorithms=["HS256"])
try:
exchange_info = jwt.decode(token, JWT_KEY, algorithms=["HS256"])

participant = DirectExchangeParticipants.objects.filter(participant=request.session["username"])
participant.update(accepted=True)
token_seconds_elapsed = time.time() - exchange_info["exp"]
if token_seconds_elapsed > VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS:
return JsonResponse({"verified": False}, safe=False, status=403)

all_participants = DirectExchangeParticipants.objects.filter(direct_exchange_id=exchange_info["exchange_id"])
participant = DirectExchangeParticipants.objects.filter(participant=request.session["username"])
participant.update(accepted=True)

all_participants = DirectExchangeParticipants.objects.filter(direct_exchange_id=exchange_info["exchange_id"])

accepted_participants = 0
for participant in all_participants:
accepted_participants += participant.accepted
accepted_participants = 0
for participant in all_participants:
accepted_participants += participant.accepted

if accepted_participants == len(all_participants):
DirectExchange.objects.filter(id=int(exchange_info["exchange_id"])).update(accepted=True)

if cache.get(token) != None:
return JsonResponse({"verified": False}, safe=False, status=403)

# Blacklist token since this token is usable only once
cache.set(
key=token,
value=token,
timeout=VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS - token_seconds_elapsed
)

if accepted_participants == len(all_participants):
DirectExchange.objects.filter(id=int(exchange_info["exchange_id"])).update(accepted=True)
return JsonResponse({"verified": True}, safe=False)

return HttpResponse()
except Exception as e:
return HttpResponse(status=500)
2 changes: 2 additions & 0 deletions docker-compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ services:
- ./django/:/usr/src/django/
ports:
- 8100:8000
depends_on:
- redis_service


fetcher:
Expand Down

0 comments on commit 6929893

Please sign in to comment.