Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixed #273 -- BACKWARDS-INCOMPATIBLE CHANGE -- Changed auth.User.pass…

…word field to add support for other password encryption algorithms. Renamed password_md5 to password and changed field length from 32 to 128. See http://code.djangoproject.com/wiki/BackwardsIncompatibleChanges for upgrade information

git-svn-id: http://code.djangoproject.com/svn/django/trunk@1327 bcc190cf-cafb-0310-a4f2-bffc1f526a37
  • Loading branch information...
commit a49fa746cdc056f0b660f47fbc55aa43fcd54bcc 1 parent f1a8869
Adrian Holovaty authored November 21, 2005
41  django/models/auth.py
@@ -34,7 +34,7 @@ class User(meta.Model):
34 34
     first_name = meta.CharField(_('first name'), maxlength=30, blank=True)
35 35
     last_name = meta.CharField(_('last name'), maxlength=30, blank=True)
36 36
     email = meta.EmailField(_('e-mail address'), blank=True)
37  
-    password_md5 = meta.CharField(_('password'), maxlength=32, help_text=_("Use an MD5 hash -- not the raw password."))
  37
+    password = meta.CharField(_('password'), maxlength=128, help_text=_("Use '[algo]$[salt]$[hexdigest]"))
38 38
     is_staff = meta.BooleanField(_('staff status'), help_text=_("Designates whether the user can log into this admin site."))
39 39
     is_active = meta.BooleanField(_('active'), default=True)
40 40
     is_superuser = meta.BooleanField(_('superuser status'))
@@ -53,7 +53,7 @@ class META:
53 53
         exceptions = ('SiteProfileNotAvailable',)
54 54
         admin = meta.Admin(
55 55
             fields = (
56  
-                (None, {'fields': ('username', 'password_md5')}),
  56
+                (None, {'fields': ('username', 'password')}),
57 57
                 (_('Personal info'), {'fields': ('first_name', 'last_name', 'email')}),
58 58
                 (_('Permissions'), {'fields': ('is_staff', 'is_active', 'is_superuser', 'user_permissions')}),
59 59
                 (_('Important dates'), {'fields': ('last_login', 'date_joined')}),
@@ -78,13 +78,35 @@ def get_full_name(self):
78 78
         return full_name.strip()
79 79
 
80 80
     def set_password(self, raw_password):
81  
-        import md5
82  
-        self.password_md5 = md5.new(raw_password).hexdigest()
  81
+        import sha, random
  82
+        algo = 'sha1'
  83
+        salt = sha.new(str(random.random())).hexdigest()[:5]
  84
+        hsh = sha.new(salt+raw_password).hexdigest()
  85
+        self.password = '%s$%s$%s' % (algo, salt, hsh)
83 86
 
84 87
     def check_password(self, raw_password):
85  
-        "Returns a boolean of whether the raw_password was correct."
86  
-        import md5
87  
-        return self.password_md5 == md5.new(raw_password).hexdigest()
  88
+        """
  89
+        Returns a boolean of whether the raw_password was correct. Handles
  90
+        encryption formats behind the scenes.
  91
+        """
  92
+        # Backwards-compatibility check. Older passwords won't include the
  93
+        # algorithm or salt.
  94
+        if '$' not in self.password:
  95
+            import md5
  96
+            is_correct = (self.password == md5.new(raw_password).hexdigest())
  97
+            if is_correct:
  98
+                # Convert the password to the new, more secure format.
  99
+                self.set_password(raw_password)
  100
+                self.save()
  101
+            return is_correct
  102
+        algo, salt, hsh = self.password.split('$')
  103
+        if algo == 'md5':
  104
+            import md5
  105
+            return hsh == md5.new(salt+raw_password).hexdigest()
  106
+        elif algo == 'sha1':
  107
+            import sha
  108
+            return hsh == sha.new(salt+raw_password).hexdigest()
  109
+        raise ValueError, "Got unknown password algorithm type in password."
88 110
 
89 111
     def get_group_permissions(self):
90 112
         "Returns a list of permission strings that this user has through his/her groups."
@@ -176,10 +198,9 @@ def get_profile(self):
176 198
 
177 199
     def _module_create_user(username, email, password):
178 200
         "Creates and saves a User with the given username, e-mail and password."
179  
-        import md5
180  
-        password_md5 = md5.new(password).hexdigest()
181 201
         now = datetime.datetime.now()
182  
-        user = User(None, username, '', '', email.strip().lower(), password_md5, False, True, False, now, now)
  202
+        user = User(None, username, '', '', email.strip().lower(), 'placeholder', False, True, False, now, now)
  203
+        user.set_password(password)
183 204
         user.save()
184 205
         return user
185 206
 
28  docs/authentication.txt
@@ -44,9 +44,9 @@ Fields
44 44
     * ``first_name`` -- Optional. 30 characters or fewer.
45 45
     * ``last_name`` -- Optional. 30 characters or fewer.
46 46
     * ``email`` -- Optional. E-mail address.
47  
-    * ``password_md5`` -- Required. An MD5 hash of the password. (Django
48  
-      doesn't store the raw password.) Raw passwords can be arbitrarily long
49  
-      and can contain any character.
  47
+    * ``password`` -- Required. A hash of, and metadata about, the password.
  48
+      (Django doesn't store the raw password.) Raw passwords can be arbitrarily
  49
+      long and can contain any character. See the "Passwords" section below.
50 50
     * ``is_staff`` -- Boolean. Designates whether this user can access the
51 51
       admin site.
52 52
     * ``is_active`` -- Boolean. Designates whether this user can log into the
@@ -167,6 +167,28 @@ Change a password with ``set_password()``::
167 167
     >>> u.set_password('new password')
168 168
     >>> u.save()
169 169
 
  170
+Passwords
  171
+---------
  172
+
  173
+**This only applies to the Django development version.** Previous versions,
  174
+such as Django 0.90, used simple MD5 hashes without password salts.
  175
+
  176
+The ``password`` field of a ``User`` object is a string in this format::
  177
+
  178
+    hashtype$salt$hash
  179
+
  180
+That's hashtype, salt and hash, separated by the dollar-sign character.
  181
+
  182
+Hashtype is either ``sha1`` (default) or ``md5``. Salt is a random string
  183
+used to salt the raw password to create the hash.
  184
+
  185
+For example::
  186
+
  187
+    sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4
  188
+
  189
+The ``User.set_password()`` and ``User.check_password()`` functions handle
  190
+the setting and checking of these values behind the scenes.
  191
+
170 192
 Anonymous users
171 193
 ---------------
172 194
 

0 notes on commit a49fa74

Please sign in to comment.
Something went wrong with that request. Please try again.