Opened 4 months ago
Last modified 2 days ago
#36546 assigned Cleanup/optimization
Deprecate django.utils.crypto.constant_time_compare()
| Reported by: | Tim Graham | Owned by: | Pravin |
|---|---|---|---|
| Component: | Utilities | Version: | dev |
| Severity: | Normal | Keywords: | |
| Cc: | JaeHyuckSa, Pravin | Triage Stage: | Someday/Maybe |
| Has patch: | no | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description
Historically it was a bespoke implementation (added in 45c7f427ce830dd1b2f636fb9c244fda9201cadb) before the stdlib implementation was preferred (58176dee88ac7c1038c7f685af023e634b143d02). Now it's just alias of secrets.compare_digest (which itself is an alias of hmac.compare_digest, which was used before 1d0bab0bfd77edcf1228d45bf654457a8ff1890d).
constant_time_compare() does call force_bytes() on its arguments but this was a workaround for Python 2.7 (7e3cf3cfd27e53ced0a1fc65a02849f78a292d3d) and no tests in Django's test suite fail with those calls removed.
Change History (23)
comment:1 by , 4 months ago
comment:2 by , 4 months ago
| Triage Stage: | Unreviewed → Accepted |
|---|
comment:3 by , 4 months ago
| Owner: | set to |
|---|---|
| Status: | new → assigned |
comment:4 by , 4 months ago
| Has patch: | set |
|---|
comment:5 by , 4 months ago
| Patch needs improvement: | set |
|---|
comment:6 by , 4 months ago
| Patch needs improvement: | unset |
|---|
comment:7 by , 3 months ago
| Patch needs improvement: | set |
|---|
comment:8 by , 3 months ago
| Patch needs improvement: | unset |
|---|
comment:9 by , 3 months ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
comment:12 by , 3 months ago
| Has patch: | unset |
|---|---|
| Resolution: | fixed |
| Status: | closed → new |
| Triage Stage: | Ready for checkin → Someday/Maybe |
Reopening following revert. See discussion in #36572
A later first step could be to replace internal usage of constant_time_compare() with compare_digest()
The deprecation itself needs more consideration
comment:13 by , 3 months ago
There are quite a few points that need further discussion, so it’s difficult to move this forward right now. I’ll unassign myself for the time being
comment:14 by , 3 months ago
| Owner: | removed |
|---|---|
| Status: | new → assigned |
comment:15 by , 3 months ago
| Cc: | added |
|---|
comment:17 by , 7 days ago
Pravin, refer to the discussion on the revert commit in comment:12. Looks like we first want to stop using it internally.
comment:18 by , 3 days ago
| Cc: | added |
|---|
comment:19 by , 3 days ago
@Simon Thanks for the more context.
I'm going to start working on this , First I will remove internal dependency on constant_time_compare.
comment:20 by , 3 days ago
| Owner: | set to |
|---|
comment:21 by , 3 days ago
just confirming the technical approach for the internal dependency removal:
- I will be using
secrets.compare_digest()for the replacement. - The implementation at the call site will include
force_bytes()on both arguments to ensure type safety:secrets.compare_digest(force_bytes(val1), force_bytes(val2)). - The definition of
constant_time_compare()itself will not be modified to avoid breaking third-party applications that rely on its current type-coercion logic.
Example of the change:
Old internal Django code:constant_time_compare(val1, val2)
New internal Django code: secrets.compare_digest(force_bytes(val1), force_bytes(val2))
I am proceeding with the internal refactoring now.
comment:22 by , 2 days ago
No, the force_bytes() is not needed in Django's use of constant_time_compare(), see ticket:36572#comment:4. The problem is only with third-party users of the function.
comment:23 by , 2 days ago
Hi Tim,
I was in that dilema and I was not so confident in removing force_bytes. But Since you confirmed it I can safely proceed ( i will no use force_bytes ). Thanks.
what about adding a warning in function constant_time_compare in file
django.utils.crypto.pyimport warnings from django.utils.deprecation import RemovedInNextVersionWarning def constant_time_compare(val1, val2): """Return True if the two strings are equal, False otherwise.""" warnings.warn( "constant_time_compare(val1, val2) is deprecated and will be removed in Django 6.0. " "`import secrets` from python standard library, then run secrets.compare_digest(val1,val2) instead.", RemovedInNextVersionWarning, stacklevel=2 ) return secrets.compare_digest(force_bytes(val1), force_bytes(val2))`