Ramifications:
- Changing the algorithm will make all the old HMACs invalid. This means
that when users upgrade, old sessions, signed cookies, and session
authentication hashes will be rejected as invalid and replaced (hence the
need for a backwards-compatibility layer of some sort).
- SHA-256 is slower than SHA-1, though hopefully no Django servers are
bottlenecked on hashing speed.
Thanks Predrag Gruevski (obi1kenobi on Github) for the report.
--
Ticket URL: <https://code.djangoproject.com/ticket/27468>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
* owner: nobody => R Sriranganathan
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:1>
* owner: R Sriranganathan => Srinivas Reddy Thatiparthy
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:2>
Comment (by Srinivas Reddy Thatiparthy):
A WIP PR has been raised here - https://github.com/django/django/pull/8773
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:3>
* has_patch: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:4>
* needs_better_patch: 0 => 1
* needs_tests: 0 => 1
Comment:
There are a number of review comments (from multiple reviewers) on the PR
that need to be addressed in order to move this patch forwards.
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:5>
* owner: Srinivas Reddy Thatiparthy => Claude Paroz
* needs_better_patch: 1 => 0
* needs_tests: 1 => 0
Comment:
In this [https://github.com/django/django/pull/12291 PR], I did the first
step of allowing different hash algorithms in the `salted_hmac()`
function, and tried to apply the new capability to the contrib.messages
CookieStorage. We can then work on other `salted_hmac()` usages if that
approach is validated.
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:6>
Comment (by Collin Anderson):
Maybe there could be a `SIGNING_HASHERS` similar to `PASSWORD_HASHERS`?
Add a "sha256:" prefix to new signatures, and a signature without an
algorithm should be assumed to be `SHA1`, similar to how a password
without an algorithm prefix is assumed to be `md5`.
That should provide a backwards-compatible transition path.
The default `SIGNING_HASHERS` could be something like
`['django.core.signing.hashers.SHA256',
'django.core.signing.hashers.SHA1']` (or maybe just `['sha256', 'sha1']`)
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:7>
Comment (by Claude Paroz):
Replying to [comment:7 Collin Anderson]:
> ...Add a "sha256:" prefix to new signatures, and a signature without an
algorithm should be assumed to be `SHA1`, similar to how a password
without an algorithm prefix is assumed to be `md5`.
That's the approach I choose for the CookieStorage part of my patch, I
also think it's a good upgrade path.
> The default `SIGNING_HASHERS` could be something like
`['django.core.signing.hashers.SHA256',
'django.core.signing.hashers.SHA1']` (or maybe just `['sha256', 'sha1']`)
Your idea about `SIGNING_HASHERS` could be handled separately. I'm not yet
convinced about use cases for that, but let's keep it in mind.
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:8>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"b5a62bd17db0e28e6842111034fa6714d0701edb" b5a62bd1]:
{{{
#!CommitTicketReference repository=""
revision="b5a62bd17db0e28e6842111034fa6714d0701edb"
Refs #27468 -- Added explicit tests for django.utils.crypto.salted_hmac()
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:9>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"50cf183d219face91822c75fa0a15fe2fe3cb32d" 50cf183d]:
{{{
#!CommitTicketReference repository=""
revision="50cf183d219face91822c75fa0a15fe2fe3cb32d"
Refs #27468 -- Added algorithm parameter to
django.utils.crypto.salted_hmac().
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:10>
Comment (by Claude Paroz):
Should we keep this only ticket to track all Django usage of `salted_hmac`
or should we create tickets for each one:
- User session auth hash (django.contrib.auth.base_user)
- Password rest tokens (django.contrib.auth.tokens)
- Cookie storage of messages (django.contrib.messages.storage.cookie)
- Session hashes (django.contrib.sessions.backends.base)
- Signing utility (django.core.signing)
I think that depending on the usage, different backwards-compatibility
strategies may be involved.
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:11>
* cc: Simon Charette (added)
Comment:
I'd definitely want to see `django.core.signing` default to `SHA256`,
happy to submit a ticket and patch if it's not already on your plate
Claude?
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:12>
Comment (by Claude Paroz):
I have [https://github.com/django/django/pull/12374 this PR] for the rest
password token part, and part of
[https://github.com/django/django/pull/12291 this PR] (which needs a
rebase) which tries to address the Signer part. Feel free to
continue/improve my work.
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:13>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"da4923ea87124102aae4455e947ce24599c0365b" da4923e]:
{{{
#!CommitTicketReference repository=""
revision="da4923ea87124102aae4455e947ce24599c0365b"
Refs #27468 -- Made PasswordResetTokenGenerator use SHA-256 algorithm.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:14>
Comment (by Claude Paroz):
Next step is [https://github.com/django/django/pull/12386 this PR]
(include algorithm in Signer signature).
.
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:15>
Comment (by Claude Paroz):
We have now two proposed strategies for handling an algorithm change in
`django.core.signing.Signer`:
- the [https://github.com/django/django/pull/12386 first one] was to
include the signer algorithm in the signature, so when unsigning, we use
that algorithm taken from the signature.
That approach was not approved by @ubernostrum, so base on his comments,
an alternative is:
- [https://github.com/django/django/pull/12454 set the algorithm] in the
Signer class (with an optional `legacy_algorithm` value to handle the
transition periods), so unsigning is forced using that algorithm, which
avoids trusting a possibly mangled algorithm in the signature itself.
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:16>
Comment (by felixxm):
I prefer the second approach ([https://github.com/django/django/pull/12454
PR]).
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:17>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"71c4fb7beb8e3293243140e4bd74e53989196440" 71c4fb7b]:
{{{
#!CommitTicketReference repository=""
revision="71c4fb7beb8e3293243140e4bd74e53989196440"
Refs #27468 -- Changed default Signer algorithm to SHA-256.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:18>
* has_patch: 1 => 0
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:19>
Comment (by felixxm):
Claude, are you going to work on the last point, i.e. user session auth
hash? I would really like to close it in Django 3.1. I can prepare it if
you're busy, just let me know. Thanks.
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:20>
* Attachment "27468-sessions.diff" added.
WIP patch for user sessions
Comment (by Claude Paroz):
Mariusz, I attached a "very" WIP patch (no tests, no docs) I was working
on some days ago. It might not even be the good approach, but feel free to
get some inspiration if it has any merit. I'll happily review your
version.
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:21>
* has_patch: 0 => 1
Comment:
Thanks! I don't think we need to persist legacy sessions in `login()`,
what's more flushing them should help not using them anymore.
[https://github.com/django/django/pull/12822 PR]
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:22>
Comment (by GitHub <noreply@…>):
In [changeset:"54646a423b4501aeb80bbdd9238f20500c84cd5f" 54646a42]:
{{{
#!CommitTicketReference repository=""
revision="54646a423b4501aeb80bbdd9238f20500c84cd5f"
Refs #27468 -- Made user sessions use SHA-256 algorithm.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:23>
* status: assigned => closed
* resolution: => fixed
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:24>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"1d6fdca557e674b9a789b51caadca8985e588492" 1d6fdca]:
{{{
#!CommitTicketReference repository=""
revision="1d6fdca557e674b9a789b51caadca8985e588492"
Refs #27468 -- Added tests and release notes for signing.dumps()/loads()
changes.
Follow up to 71c4fb7beb8e3293243140e4bd74e53989196440.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:25>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"b84b1921da23ad0a66e40f16030294c416a0e98b" b84b192]:
{{{
#!CommitTicketReference repository=""
revision="b84b1921da23ad0a66e40f16030294c416a0e98b"
[3.1.x] Refs #27468 -- Added tests and release notes for
signing.dumps()/loads() changes.
Follow up to 71c4fb7beb8e3293243140e4bd74e53989196440.
Backport of 1d6fdca557e674b9a789b51caadca8985e588492 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:26>
Comment (by GitHub <noreply@…>):
In [changeset:"7c929fcf7c8913d0bd78a8400eafe866ceed9aa6" 7c929fcf]:
{{{
#!CommitTicketReference repository=""
revision="7c929fcf7c8913d0bd78a8400eafe866ceed9aa6"
Refs #27468 -- Fixed TestSigner.test_dumps_loads_legacy_signature.
Added in 1d6fdca557e674b9a789b51caadca8985e588492.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:27>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"c67314ff9561dec138dac020d071718110abd0ce" c67314ff]:
{{{
#!CommitTicketReference repository=""
revision="c67314ff9561dec138dac020d071718110abd0ce"
[3.1.x] Refs #27468 -- Fixed TestSigner.test_dumps_loads_legacy_signature.
Added in 1d6fdca557e674b9a789b51caadca8985e588492.
Backport of 7c929fcf7c8913d0bd78a8400eafe866ceed9aa6 from master
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:28>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"66b4046d68921edc7c83076da4aafd307f7dd19b" 66b4046d]:
{{{
#!CommitTicketReference repository=""
revision="66b4046d68921edc7c83076da4aafd307f7dd19b"
Refs #27468 -- Removed support for the pre-Django 3.1 password reset
tokens.
Per deprecation timeline.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:29>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"d32a232fe92e0162030c7905f877d8a07c09e6c7" d32a232f]:
{{{
#!CommitTicketReference repository=""
revision="d32a232fe92e0162030c7905f877d8a07c09e6c7"
Refs #27468 -- Removed support for the pre-Django 3.1 signatures in Signer
and signing.dumps()/loads().
Per deprecation timeline.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:30>
Comment (by Mariusz Felisiak <felisiak.mariusz@…>):
In [changeset:"6b4941dd577c494cfa49dbeacfd33594ae770047" 6b4941dd]:
{{{
#!CommitTicketReference repository=""
revision="6b4941dd577c494cfa49dbeacfd33594ae770047"
Refs #27468 -- Removed support for the pre-Django 3.1 user sessions.
Per deprecation timeline.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:31>
Comment (by GitHub <noreply@…>):
In [changeset:"a94ae4cb11b2b6a6fffb26f5a2dfd0c665e2070d" a94ae4cb]:
{{{
#!CommitTicketReference repository=""
revision="a94ae4cb11b2b6a6fffb26f5a2dfd0c665e2070d"
Refs #27468 -- Updated django.core.signing docstring.
Follow up to 71c4fb7beb8e3293243140e4bd74e53989196440.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/27468#comment:32>