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
This commit is contained in:
Robert Sparks 2022-09-20 12:09:11 -05:00 committed by GitHub
parent 5d6f1e7509
commit 5c8545eecb
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 24 additions and 8 deletions

View file

@ -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 -*- # -*- coding: utf-8 -*-
import base64
import datetime import datetime
import hashlib import hashlib
import hmac
import os import os
import re import re
import tempfile import tempfile
@ -101,8 +103,7 @@ def get_user_email(user):
return user._email_cache return user._email_cache
def get_hash_nominee_position(date, nominee_position_id): 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): def initialize_templates_for_group(group):
for template_name in DEFAULT_NOMCOM_TEMPLATES: for template_name in DEFAULT_NOMCOM_TEMPLATES:
@ -165,6 +166,8 @@ def delete_nomcom_templates(nomcom):
nomcom_template_path = '/nomcom/' + nomcom.group.acronym nomcom_template_path = '/nomcom/' + nomcom.group.acronym
DBTemplate.objects.filter(path__contains=nomcom_template_path).delete() 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): def retrieve_nomcom_private_key(request, year):
private_key = request.session.get('NOMCOM_PRIVATE_KEY_%s' % year, None) 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 return private_key
command = "%s bf -d -in /dev/stdin -k \"%s\" -a" command = "%s bf -d -in /dev/stdin -k \"%s\" -a"
code, out, error = pipe(command % (settings.OPENSSL_COMMAND, code, out, error = pipe(
settings.SECRET_KEY), private_key) command % (
settings.OPENSSL_COMMAND,
command_line_safe_secret(settings.NOMCOM_APP_SECRET)
),
private_key
)
if code != 0: if code != 0:
log("openssl error: %s:\n Error %s: %s" %(command, code, error)) log("openssl error: %s:\n Error %s: %s" %(command, code, error))
return out return out
@ -185,8 +193,13 @@ def store_nomcom_private_key(request, year, private_key):
request.session['NOMCOM_PRIVATE_KEY_%s' % year] = '' request.session['NOMCOM_PRIVATE_KEY_%s' % year] = ''
else: else:
command = "%s bf -e -in /dev/stdin -k \"%s\" -a" command = "%s bf -e -in /dev/stdin -k \"%s\" -a"
code, out, error = pipe(command % (settings.OPENSSL_COMMAND, code, out, error = pipe(
settings.SECRET_KEY), private_key) command % (
settings.OPENSSL_COMMAND,
command_line_safe_secret(settings.NOMCOM_APP_SECRET)
),
private_key
)
if code != 0: if code != 0:
log("openssl error: %s:\n Error %s: %s" %(command, code, error)) log("openssl error: %s:\n Error %s: %s" %(command, code, error))
if error: if error:

View file

@ -6,6 +6,7 @@ import datetime
import re import re
from collections import OrderedDict, Counter from collections import OrderedDict, Counter
import csv import csv
import hmac
from django.conf import settings from django.conf import settings
from django.contrib import messages 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): 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: if not valid:
permission_denied(request, "Bad hash!") permission_denied(request, "Bad hash!")
expiration_days = getattr(settings, 'DAYS_TO_EXPIRE_NOMINATION_LINK', None) expiration_days = getattr(settings, 'DAYS_TO_EXPIRE_NOMINATION_LINK', None)

View file

@ -1272,6 +1272,8 @@ if SERVER_MODE != 'production':
if 'SECRET_KEY' not in locals(): if 'SECRET_KEY' not in locals():
SECRET_KEY = 'PDwXboUq!=hPjnrtG2=ge#N$Dwy+wn@uivrugwpic8mxyPfHka' 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 = ['*',] ALLOWED_HOSTS = ['*',]