-
-
Notifications
You must be signed in to change notification settings - Fork 30.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HMAC default to MD5 marked as to be removed in 3.6 #77785
Comments
HMAC docs says digestmod=md5 default will be removed in 3.6, but was not. We should likely bumpt that to 3.8 in the documentation, and actually remove it in 3.8 |
I've sent two PRs, one that updates the deprecation from PendingDeprecationWarning to DeprecationWarning that likely should get applied to 3.6, and 3.7 ? And a PR to actually remove the default in 3.8. |
The docs still make it look like *digestmod* is an optional argument: The help output does as well: >>> help(hmac.new)
Help on function new in module hmac: new(key, msg=None, digestmod=None)
Create a new hashing object and return it.
key: The starting key for the hash.
msg: if available, will immediately be hashed into the object's starting
state.
You can now feed arbitrary strings into the object using its update()
method, and can ask for the hash value at any time by calling its digest()
method. Also, it is well outside the Python norms to have a required argument default to None and having that default value be invalid. Presumably, the type annotation for this would be, "digestmod: Optional[str]=None". That would further add to the confusion with a required Optional argument. Another thought: The usual exception for a missing argument is a TypeError, not a ValueError Lastly, I'm curious why another algorithm wasn't used (perhaps sha256) as a default rather than removing the default altogether. This doesn't seems like good API design. FWIW, this removal broke the third-party package, Bottle:
127.0.0.1 - - [15/Oct/2019 07:53:10] "GET / HTTP/1.1" 200 1471
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/bottle.py", line 862, in _handle
return route.call(**args)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/bottle.py", line 1742, in wrapper
rv = callback(*a, **ka)
File "webapp.py", line 32, in check_credentials
response.set_cookie('token', token, max_age=3600, secret=secret)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/bottle.py", line 1626, in set_cookie
value = touni(cookie_encode((name, value), secret))
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/bottle.py", line 2600, in cookie_encode
sig = base64.b64encode(hmac.new(tob(key), msg).digest())
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/hmac.py", line 146, in new
return HMAC(key, msg, digestmod)
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/hmac.py", line 49, in __init__
raise ValueError('`digestmod` is required.')
ValueError: `digestmod` is required. |
The weird argument style of a required digestmod with None as default is an unfortunate outcome of the old API. The msg and digestmod argument can be passed in as keyword and as positional argument. I studied existing code and have considered to make digestmod a required keyword-only argument, but that would have broken too much code. The current style is backwards compatible with all code except for code that must be changed any way. Only code that depends on implicit default digestmod="md5" breaks. The code must adjusted for the deprecation no matter the argument style. The required change is fully backwards compatible with Python 2.7 to 3.7. Bottle is such a case that got broken by the deprecation. It does not make sense to default to another hashing algorithm:
|
Thanks for the feedback. Better late than never. :) A default algorithm is a bad thing when it comes to authentication. Explicit is better than implicit. A default regularly becomes obsolete as math and cryptanalysis methods move forward and need to be changed every unpredictable N years. MD5 was _already_ a bad choice of default when hmac was added in 2.2. That said, we managed this deprecation and API evolution poorly. As it has shipped this way in 3.8, I'm first going to fix the documentation and the exception type (both suitable for 3.8). First PR sent. In 3.9 we could introduce a better named keyword only digest parameter, leaving digestmod supported as a legacy positional & alternate name for backwards incompatibility. (minor code gymnastics required to do that, but within reason) i wouldn't want to remove the digestmod positional/name support until after 3.8 is no longer relevant in the world. |
BTW, if you want the type annotation that'd be used for this, 3.8 effectively removes the Optional[] from the one listed in: https://github.com/python/typeshed/blob/master/stdlib/2and3/hmac.pyi#L16 |
IMV, the adopted solution creates a problem of a mismatch between the signature and the function behavior. I was just hit by this, as I never cared to specify digestmod trusting that Python defaults are usually sound choices. I agree that changing the signature would break more code, but the choice made here violates the principle of least astonishment. An error that could be caught by a static analysis tool silently entered the code base to be provoked only when the function got called. The choice to break compatibility was already made. Introducing silent bugs is something that I think we should avoid. I've wrote an argument about this in a different message, and I'm not sure if I should repeat it here. Here's the link to it: |
Noted, but making it a keyword only argument would break a lot of existing code that has always just been passing three positional args. Needless pain. |
Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.
Show more details
GitHub fields:
bugs.python.org fields:
The text was updated successfully, but these errors were encountered: