I have a Django 2.2 project that runs in a bunch of different servers, but they use the same database.
I've created a branch to migrate to Django 3, but not all servers will be migrated at the same time.
I use Argon2:
# https://docs.djangoproject.com/en/dev/ref/settings/#password-hashers
PASSWORD_HASHERS = [
'django.contrib.auth.hashers.Argon2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2PasswordHasher',
'django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher',
'django.contrib.auth.hashers.BCryptSHA256PasswordHasher',
'django.contrib.auth.hashers.BCryptPasswordHasher',
]
When I switched to django 3.2 in my developing branch everything worked fine. But then, when I returned to Django 2.2, I started getting errors like:
- Template syntax error
- Incorrect padding (
exception location: .../python3.6/base64.py in b64decode
)
Those problems where solved by just deleting the cookies and reloading. So I guessed that they were related to the change in django 3.1 to a new default hashing algorithm from sha1 to sha256.
Anyway, after reloading, the page worked. But when I tried to login it didn't recognize the credentials.
Then I restored the database from backup and could login in django 2.2.
I tried again to run on django 3.2 with the setting:
DEFAULT_HASHING_ALGORITHM = 'sha1'
Now, when switching back to 2.2, I didn't get errors on page load (I didn't need to delete cookies) but the credentials still didn't work.
For me it looks like after switching to django 3.2, the hashing of the passwords in the database are changed.
Is it possible that django 3 rewrites passwords in the database? Can anybody point to a solution or something to try?
Thank you.
Solution TL;DR
Well, it seems that if you use the Argon2 hasher, Django does indeed update the stored password and if you want to avoid it temporarily you must update to Django 3.1 until ready to move to 3.2 or subclass the hasher.
Comparing stored passwords
Since I can access the production database (django 2.2) and the local one when in Django 3, I compare the passwords on my user:
Django 2.2 (released April 2019)
algorithm: argon2 type: argon2i version: 19 memory cost: 512 time cost: 2 parallelism: 2 salt: UVn ********** hash: QVt *****************
Django 3.2
algorithm: argon2 variety: argon2id version: 19 memory cost: 102,400 time cost: 2 parallelism: 8 salt: pHQzc2 **************** hash: flj ****************
Yes, they are different!
Django 3.2 release notes tell about changes in the default Argon2 hasher:
This is the ticket of that change:
#30472 Argon2id should be supported and become the default variety for Argon2PasswordHasher
And looking at the code in
django.contrib.auth.hashers
, I can see that the passwords are modified oncheck_password
:This line
type=argon2.low_level.Type.ID
changes argon2 type fromargon2i
toargon2id
. The rest of the changes are clear.I`m not sure if this the real process but I guess it goes something like this:
(I'll be glad to know if I'm wrong)
To recap: My problem
My problem is that I have several different Django 2 projects using the same common core code and the same database. Although they are different projects, there are many users who have access to all of them. I want to update them progressively starting with the least sensitive to see if errors arise.
Solution 1
Upgrade to Django 3.1 instead of 3.2. That would allow me to update the different projects progressively without breaking user access along the way. Once all the projects have been running for a while with version 3.1 and fixed any bugs that have emerged, I can update them all at the same time to django 3.2 with greater confidence. This is what I have tested and works (it doesn't change passwords).
Solution 2
Subclass the
django.contrib.auth.hashers.Argon2PasswordHasher
hasher so it doesn't update passwords. Point to it in thePASSWORD_HASHERS
settings and remove it when all projects are running smoothly in django 3.2.