Migrating ASP.NET membership users to Django without resetting passwords?
I've got a system that was partially written by someone else and is a complete maintenance nightmare for such a small app. I've finally been given changes which justifies just rewriting the horrible mess so I am moving it to Django.
Before I take the plunge, I've been trying to move over the password hash and salt into the Django auth tables [sha1]$[salt]$[hash] but can't get it to hash properly (resetting passwords isn't really an option).
Here is what I've been able to find out so far:
- ASP.NET stores the hash as base64 string and uses a base64 salt (before hash)
- I can obviously reverse the base64 hash to a byte array
- Django uses a hexdigest, I tried BitConverter.ToString but they has开发者_JAVA技巧h differently
Am I fighting a losing battle here? Would it be better to write a method in Django to hash as ASP.NET does?
Any help appreciated,
Thomas
The only real options you have here to avoid a password reset in the middle is to:
- Write a hash translation algorithm to transfer the Asp.Net hash into the hexdigest hash. Good luck with this one. If you pull it off, write a paper about it.
- Rewrite the Django hashing algorithm to hash identically to the Asp.Net algorithm. This one should be the easiest to pull off, but it's still going to have its traps and pitfalls in the process.
You could also attempt to reverse engineer the passwords, but if you're successful in doing that it sort of makes the hashing algorithm pointless IMO.
Create a new module with the following Django password hasher to handle ASP.net (sha1) passwords:
import hashlib
import base64
from django.contrib.auth.hashers import (BasePasswordHasher, mask_hash)
from django.utils.datastructures import SortedDict
from django.utils.encoding import force_bytes
from django.utils.crypto import constant_time_compare
from django.utils.translation import ugettext_noop as _
def utf16tobin(s):
return s.encode('hex')[4:].decode('hex')
class MSSHA1PasswordHasher(BasePasswordHasher):
"""
ASP.NET hasher
"""
algorithm = "mssha1"
def encode(self, password, salt):
assert password is not None
assert salt and '$' not in salt
pwdenc = password.encode('utf16')
pwdenc = utf16tobin(pwdenc)
saltdecode = base64.b64decode(salt)
m = hashlib.sha1()
m.update(saltdecode)
m.update(pwdenc)
hash = base64.b64encode(m.digest())
return "%s$%s$%s" % (self.algorithm, salt, hash)
def verify(self, password, encoded):
algorithm, salt, hash = encoded.split('$', 2)
assert algorithm == self.algorithm
encoded_2 = self.encode(password, salt)
return constant_time_compare(encoded, encoded_2)
def safe_summary(self, encoded):
algorithm, salt, hash = encoded.split('$', 2)
assert algorithm == self.algorithm
return SortedDict([
(_('algorithm'), algorithm),
(_('salt'), mask_hash(salt, show=2)),
(_('hash'), mask_hash(hash)),
])
Add the module name to your settings file in the PASSWORD_HASHERS list (see https://docs.djangoproject.com/en/1.4/topics/auth/ for details).
Migrate the ASP.net salt and password into the Django password field like this:
user.password = "mssha1$" + old_membership.passwordsalt + "$" + old_membership.password
Users can then login to your Django app with their existing ASP.net passwords. Once they've logged in successfully, Django will automatically upgrade their passwords to the latest algorithm, e.g. PBKDF2.
精彩评论