{"id":451,"date":"2011-09-10T15:09:49","date_gmt":"2011-09-10T20:09:49","guid":{"rendered":"http:\/\/www.insomnihack.com\/?p=451"},"modified":"2011-09-20T14:29:51","modified_gmt":"2011-09-20T19:29:51","slug":"enforce-strong-django-passwords","status":"publish","type":"post","link":"https:\/\/www.insomnihack.com\/?p=451","title":{"rendered":"Enforce Strong Django Passwords"},"content":{"rendered":"<p>We&#8217;re using <a title=\"Django Project\" href=\"https:\/\/www.djangoproject.com\/\">Django<\/a> 1.3 for a new project at work. One of the things Django provides us with out of the box are\u00a0convenient\u00a0password change\/reset <a title=\"Django authentication forms\" href=\"https:\/\/docs.djangoproject.com\/en\/1.3\/topics\/auth\/#module-django.contrib.auth.forms\">forms<\/a> and <a title=\"Django authentication views\" href=\"https:\/\/docs.djangoproject.com\/en\/1.3\/topics\/auth\/#module-django.contrib.auth.views\">views<\/a>. However, they don&#8217;t enforce any kind of password strength. As the application touches on financial data, it was suggested we nudge our users towards stronger passwords.<\/p>\n<p>A quick search found <a title=\"django-passwords on PyPI\" href=\"http:\/\/pypi.python.org\/pypi\/django-passwords\/\">django-passwords<\/a> by <a title=\"Donald Stufft's GitHub profile\" href=\"https:\/\/github.com\/dstufft\">Donald Stufft<\/a>. The source code can be found <a title=\"django-passwords on GitHub\" href=\"https:\/\/github.com\/dstufft\/django-passwords\/\">here<\/a>.<\/p>\n<p>The django-passwords application provides a PasswordField and validators for enforcing stronger passwords with a number of options. I&#8217;ll show you what I did to integrate it with Django&#8217;s built-in password change and reset views.<\/p>\n<p>First, install django-passwords with with pip or easy_install. Next, edit your settings.py file and add the application and your preferred password strength settings.<\/p>\n<p>[python]<br \/>\nINSTALLED_APPS = (<br \/>\n    &#8216;django.contrib.auth&#8217;,<br \/>\n    &#8216;django.contrib.contenttypes&#8217;,<br \/>\n    &#8216;django.contrib.sessions&#8217;,<br \/>\n    &#8216;django.contrib.sites&#8217;,<br \/>\n    &#8216;django.contrib.messages&#8217;,<br \/>\n    &#8216;django.contrib.staticfiles&#8217;,<br \/>\n    # Uncomment the next line to enable the admin:<br \/>\n    &#8216;django.contrib.admin&#8217;,<br \/>\n    # Uncomment the next line to enable admin documentation:<br \/>\n    # &#8216;django.contrib.admindocs&#8217;,<br \/>\n    &#8216;passwords&#8217;,<br \/>\n)<\/p>\n<p># Minimum password strength settings. See the GitHub page for defaults.<br \/>\n# https:\/\/github.com\/dstufft\/django-passwords\/<br \/>\nPASSWORD_MIN_LENGTH = 8<br \/>\nPASSWORD_COMPLEXITY = { &quot;UPPER&quot;:\u00a0 1, &quot;LOWER&quot;:\u00a0 1, &quot;DIGITS&quot;: 1 }<br \/>\n[\/python]<\/p>\n<p>In the example above, I&#8217;ve configured settings.py file to require passwords that are at least 8 characters long and contain at least one uppercase, one lowercase, and one numeric character. Now, any place I use a PasswordField, text entered will be validated against these requirements.<\/p>\n<p>If you wish to use Django&#8217;s included password reset\/change views, you have one more step do. <del>You&#8217;ll need to extend <em>django.contrib.auth.forms.SetPasswordForm<\/em> and <em>django.contrib.auth.forms.PasswordChangeForm<\/em> and add the validate_length, complexity, dictionary_words, and common_sequences validators to those forms&#8217; <em>new_password1<\/em> CharFields.<\/del><\/p>\n<p><strong>EDIT<\/strong>: Matt Austin was kind enough to point out that all your really need to do is override one of the password fields in SetPasswordForm and PasswordChangeForm with PasswordField, so I&#8217;ve replaced my convoluted brain-dead implementation with his suggestion below.<\/p>\n<p>[python]<br \/>\nfrom django.contrib.auth.forms import SetPasswordForm, PasswordChangeForm<br \/>\nfrom django.utils.translation import ugettext_lazy as _<br \/>\nfrom passwords.fields import PasswordField<\/p>\n<p>class ValidatingSetPasswordForm(SetPasswordForm):<br \/>\n    new_password2 = PasswordField(label=_(&quot;New password confirmation&quot;))<\/p>\n<p>class ValidatingPasswordChangeForm(PasswordChangeForm):<br \/>\n    new_password2 = PasswordField(label=_(&quot;New password confirmation&quot;))<br \/>\n[\/python]<\/p>\n<p>I put mine in a forms.py file in my application package. Finally, you need to replace the default forms with the new validating versions in the password routes of your urls.py file.<\/p>\n<p>[python]<br \/>\nurl(r&#8217;^password_change\/$&#8217;, &#8216;django.contrib.auth.views.password_change&#8217;,<br \/>\n    {&#8216;password_change_form&#8217;: TimeStampedPasswordChangeForm}),<br \/>\nurl(r&#8217;^password_changed\/$&#8217;, &#8216;django.contrib.auth.views.password_change_done&#8217;),<br \/>\nurl(r&#8217;^password_reset\/$&#8217;, &#8216;django.contrib.auth.views.password_reset&#8217;),<br \/>\nurl(r&#8217;^password_reset_done\/$&#8217;, &#8216;django.contrib.auth.views.password_reset_done&#8217;),<br \/>\nurl(r&#8217;^password_reset_complete\/$&#8217;, &#8216;django.contrib.auth.views.password_reset_complete&#8217;),<br \/>\nurl(r&#8217;^password_reset_confirm\/(?P&lt;uidb36&gt;[-\\w]+)\/(?P&lt;token&gt;[-\\w]+)\/$&#8217;,<br \/>\n    &#8216;django.contrib.auth.views.password_reset_confirm&#8217;,<br \/>\n    {&#8216;set_password_form&#8217;: TimeStampedSetPasswordForm}),<br \/>\n[\/python]<\/p>\n<p>Congratulations, your site should now be enforcing the heck out of user passwords. However, let&#8217;s not get too full of ourselves. XKCD explains why this probably isn&#8217;t solving the real problem of password security anyway.<\/p>\n<p style=\"text-align: center;\"><a href=\"http:\/\/xkcd.com\/936\/\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter\" title=\"Password Strength\" src=\"http:\/\/imgs.xkcd.com\/comics\/password_strength.png\" alt=\"XCKC Password Strength comic\" width=\"550\" height=\"447\" \/><\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>We&#8217;re using Django 1.3 for a new project at work. One of the things Django provides us with out of the box are\u00a0convenient\u00a0password change\/reset forms and views. However, they don&#8217;t enforce any kind of password strength. As the application touches on financial data, it was suggested we nudge our users towards stronger passwords. A quick [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[36,6],"_links":{"self":[{"href":"https:\/\/www.insomnihack.com\/index.php?rest_route=\/wp\/v2\/posts\/451"}],"collection":[{"href":"https:\/\/www.insomnihack.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.insomnihack.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.insomnihack.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.insomnihack.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=451"}],"version-history":[{"count":0,"href":"https:\/\/www.insomnihack.com\/index.php?rest_route=\/wp\/v2\/posts\/451\/revisions"}],"wp:attachment":[{"href":"https:\/\/www.insomnihack.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=451"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.insomnihack.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=451"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.insomnihack.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=451"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}