From 426736b8288e40a6c283e653c73e59efbc436c4f Mon Sep 17 00:00:00 2001 From: Marian Sigler Date: Thu, 23 May 2013 00:17:45 +0200 Subject: use a constant time compare when verifying confirmation links --- utils.py | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) (limited to 'utils.py') diff --git a/utils.py b/utils.py index 7071284..7e5fd85 100644 --- a/utils.py +++ b/utils.py @@ -13,6 +13,7 @@ from flask import current_app, flash, g, redirect, render_template, request, ses from flask import url_for as flask_url_for from flask.ext.wtf import ValidationError from hashlib import sha1 +from itertools import izip from random import randint from time import time from werkzeug.exceptions import Forbidden @@ -148,7 +149,7 @@ def verify_confirmation(realm, token, timeout=None): tokentime = struct.unpack('>i', token[20:24])[0] payload = token[24:] - if mac != hmac.new(key, token[20:], sha1).digest(): + if not constant_time_compare(mac, hmac.new(key, token[20:], sha1).digest()): raise ConfirmationInvalid('MAC does not match') if timeout is not None and time() > tokentime + timeout: @@ -170,6 +171,31 @@ def http_verify_confirmation(*args, **kwargs): raise Forbidden(u'Bestätigungslink ist zu alt.') +# Shamelessly stolen from https://github.com/mitsuhiko/itsdangerous/ +# (C) 2011 by Armin Ronacher and the Django Software Foundation. 3-clause BSD. +def constant_time_compare(val1, val2): + """Returns True if the two strings are equal, False otherwise. + + The time taken is independent of the number of characters that match. Do + not use this function for anything else than comparision with known + length targets. + + This is should be implemented in C in order to get it completely right. + """ + if _builtin_constant_time_compare is not None: + return _builtin_constant_time_compare(val1, val2) + len_eq = len(val1) == len(val2) + if len_eq: + result = 0 + left = val1 + else: + result = 1 + left = val2 + for x, y in izip(bytearray(left), bytearray(val2)): + result |= x ^ y + return result == 0 + + def send_mail(recipient, subject, body, sender=None): if sender is None: sender = current_app.config['MAIL_DEFAULT_SENDER'] -- cgit v1.2.3-1-g7c22