From 5c8545eecbe4f7d2084b7a5c93bb449f63d56675 Mon Sep 17 00:00:00 2001 From: Robert Sparks Date: Tue, 20 Sep 2022 12:09:11 -0500 Subject: [PATCH] feat: use hmac, sha256, and a better secret for published nomcom hashes. (#4475) * feat: use hmac, sha256, and a better secret for published nomcom hashes. * fix: avoid using django secret key for a different secret context * fix: Only strip the newline * fix: improve readability by using rstrip --- ietf/nomcom/utils.py | 27 ++++++++++++++++++++------- ietf/nomcom/views.py | 3 ++- ietf/settings.py | 2 ++ 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/ietf/nomcom/utils.py b/ietf/nomcom/utils.py index f9066143b..4f781e385 100644 --- a/ietf/nomcom/utils.py +++ b/ietf/nomcom/utils.py @@ -1,9 +1,11 @@ -# Copyright The IETF Trust 2012-2020, All Rights Reserved +# Copyright The IETF Trust 2012-2022, All Rights Reserved # -*- coding: utf-8 -*- +import base64 import datetime import hashlib +import hmac import os import re import tempfile @@ -101,8 +103,7 @@ def get_user_email(user): return user._email_cache def get_hash_nominee_position(date, nominee_position_id): - return hashlib.md5(('%s%s%s' % (settings.SECRET_KEY, date, nominee_position_id)).encode('utf-8')).hexdigest() - + return hmac.new(settings.NOMCOM_APP_SECRET, f"{date}{nominee_position_id}".encode('utf-8'), hashlib.sha256).hexdigest() def initialize_templates_for_group(group): for template_name in DEFAULT_NOMCOM_TEMPLATES: @@ -165,6 +166,8 @@ def delete_nomcom_templates(nomcom): nomcom_template_path = '/nomcom/' + nomcom.group.acronym DBTemplate.objects.filter(path__contains=nomcom_template_path).delete() +def command_line_safe_secret(secret): + return base64.encodebytes(secret).decode('utf-8').rstrip() def retrieve_nomcom_private_key(request, year): private_key = request.session.get('NOMCOM_PRIVATE_KEY_%s' % year, None) @@ -173,8 +176,13 @@ def retrieve_nomcom_private_key(request, year): return private_key command = "%s bf -d -in /dev/stdin -k \"%s\" -a" - code, out, error = pipe(command % (settings.OPENSSL_COMMAND, - settings.SECRET_KEY), private_key) + code, out, error = pipe( + command % ( + settings.OPENSSL_COMMAND, + command_line_safe_secret(settings.NOMCOM_APP_SECRET) + ), + private_key + ) if code != 0: log("openssl error: %s:\n Error %s: %s" %(command, code, error)) return out @@ -185,8 +193,13 @@ def store_nomcom_private_key(request, year, private_key): request.session['NOMCOM_PRIVATE_KEY_%s' % year] = '' else: command = "%s bf -e -in /dev/stdin -k \"%s\" -a" - code, out, error = pipe(command % (settings.OPENSSL_COMMAND, - settings.SECRET_KEY), private_key) + code, out, error = pipe( + command % ( + settings.OPENSSL_COMMAND, + command_line_safe_secret(settings.NOMCOM_APP_SECRET) + ), + private_key + ) if code != 0: log("openssl error: %s:\n Error %s: %s" %(command, code, error)) if error: diff --git a/ietf/nomcom/views.py b/ietf/nomcom/views.py index 5448c1a3b..9f752e49e 100644 --- a/ietf/nomcom/views.py +++ b/ietf/nomcom/views.py @@ -6,6 +6,7 @@ import datetime import re from collections import OrderedDict, Counter import csv +import hmac from django.conf import settings from django.contrib import messages @@ -695,7 +696,7 @@ def private_questionnaire(request, year): def process_nomination_status(request, year, nominee_position_id, state, date, hash): - valid = get_hash_nominee_position(date, nominee_position_id) == hash + valid = hmac.compare_digest(get_hash_nominee_position(date, nominee_position_id), hash) if not valid: permission_denied(request, "Bad hash!") expiration_days = getattr(settings, 'DAYS_TO_EXPIRE_NOMINATION_LINK', None) diff --git a/ietf/settings.py b/ietf/settings.py index c41d4ae0f..aed1f75c7 100644 --- a/ietf/settings.py +++ b/ietf/settings.py @@ -1272,6 +1272,8 @@ if SERVER_MODE != 'production': if 'SECRET_KEY' not in locals(): SECRET_KEY = 'PDwXboUq!=hPjnrtG2=ge#N$Dwy+wn@uivrugwpic8mxyPfHka' + if 'NOMCOM_APP_SECRET' not in locals(): + NOMCOM_APP_SECRET = b'\x9b\xdas1\xec\xd5\xa0SI~\xcb\xd4\xf5t\x99\xc4i\xd7\x9f\x0b\xa9\xe8\xfeY\x80$\x1e\x12tN:\x84' ALLOWED_HOSTS = ['*',]