Opened 5 months ago

Closed 5 months ago

#37078 closed Cleanup/optimization (fixed)

Change default algorithm of salted_hmac() from SHA-1 to SHA-256

Reported by: Denny Biasiolli Owned by: Denny Biasiolli
Component: Utilities Version: dev
Severity: Normal Keywords: security, crypto
Cc: Denny Biasiolli Triage Stage: Ready for checkin
Has patch: yes Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

The salted_hmac() function (crypto.py:19) defaults to algorithm="sha1". While HMAC-SHA1 is not cryptographically broken (HMAC construction is resistant to collision attacks), SHA-1 is deprecated by NIST and modern security standards recommend SHA-256 or stronger for all new applications.

All security-sensitive callers within Django already override this default — Signer uses sha256 (signing.py:193), PasswordResetTokenGenerator passes sha256 explicitly, and session auth hashes use SHA-256. However, any third-party code or custom application calling salted_hmac() without specifying an algorithm will silently use SHA-1.

## Steps to Reproduce

  1. In any Django project, call: `python from django.utils.crypto import salted_hmac mac = salted_hmac("my_salt", "my_value") print(mac.digest_size) # 20 bytes = SHA-1 `
  2. Observe the HMAC uses SHA-1 without any explicit algorithm selection

## Expected Behavior

salted_hmac() should default to "sha256" to match modern cryptographic best practices and align with Django's own internal usage.

## Actual Behavior

salted_hmac() defaults to algorithm="sha1" (line 19 of crypto.py).

Change History (11)

comment:1 by Denny Biasiolli, 5 months ago

Has patch: set

comment:2 by Tim Graham, 5 months ago

Previous work was done in #27468. We cannot just change default value of the parameter due to backward compatibility. The change would have to go through a deprecation.

comment:3 by Jacob Walls, 5 months ago

A deprecation might make sense, but just to check my understanding -- Tim, does it make a difference that base64_hmac() isn't documented? (And that all uses in Django have already migrated?)

comment:4 by Tim Graham, 5 months ago

Has patch: unset

There's at least one usage of base64_hmac() in ​healthchecks. Even if undocumented, I wouldn't modify security-related functionality lightly.

comment:5 by Jacob Walls, 5 months ago

Summary: salted_hmac() defaults to SHA-1 algorithm despite SHA-256 being preferred everywhere else → Change default algorithm of salted_hmac() from SHA-1 to SHA-256
Triage Stage: Unreviewed → Accepted
Version: → dev

Makes good sense -- I agree we should go through a deprecation here.

​NIST advising all uses of SHA-1 to be replaced by 2030.

comment:6 by Denny Biasiolli, 5 months ago

I'd be happy to help, but do you have suggestions about the steps I need to take to fix this?
My PR with the fix (without the deprecation) was here: ​https://github.com/django/django/pull/21190

comment:7 by Jacob Walls, 5 months ago

Sure thing, and thanks for the offer.

  1. Set yourself in the owner field here on this ticket.
  2. Check the ​deprecation guide for places to add documentation.
  3. I assume you will need to do something like change the default value for the argument to a NOT_PROVIDED sentinel, check for it, and issue the warning and fall back to SHA-1.

comment:8 by Denny Biasiolli, 5 months ago

Owner: set to Denny Biasiolli
Status: new → assigned

comment:9 by Denny Biasiolli, 5 months ago

Has patch: set

comment:10 by Jacob Walls, 5 months ago

Triage Stage: Accepted → Ready for checkin

comment:11 by Jacob Walls <jacobtylerwalls@…>, 5 months ago

Resolution: → fixed
Status: assigned → closed

In 0f4fff79:

Fixed #37078 -- Deprecated SHA-1 default for salted_hmac() and base64_hmac() algorithm.

Deprecated the default value of the algorithm argument in
django.utils.crypto.salted_hmac() and django.core.signing.base64_hmac(),
which will change from 'sha1' to 'sha256' in Django 7.0.

Note: See TracTickets for help on using tickets.
Back to Top