{"title":"Password management in Django","version":"1.10","locale":"es","docname":"topics/auth/passwords","url":"/es/1.10/topics/auth/passwords/","canonical":"https://djangodocs.dev/es/1.10/topics/auth/passwords/","summary":"Password management is something that should generally not be reinvented unnecessarily, and Django endeavors to provide a secure and flexible set of tools for…","html":"<h1>Password management in Django<a class=\"heading-anchor\" href=\"#password-management-in-django\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h1>\n<p>Password management is something that should generally not be reinvented\nunnecessarily, and Django endeavors to provide a secure and flexible set of\ntools for managing user passwords. This document describes how Django stores\npasswords, how the storage hashing can be configured, and some utilities to\nwork with hashed passwords.</p>\n<aside class=\"admonition admonition-seealso\">\n<p class=\"admonition-title\">Ver también</p>\n<p>Even though users may use strong passwords, attackers might be able to\neavesdrop on their connections. Use <a class=\"reference internal\" href=\"/es/1.10/topics/security/#security-recommendation-ssl\"><span class=\"std std-ref\">HTTPS</span></a> to avoid sending passwords (or any other\nsensitive data) over plain HTTP connections because they will be vulnerable\nto password sniffing.</p>\n</aside>\n<section id=\"how-django-stores-passwords\">\n<span id=\"auth-password-storage\"></span><h2>How Django stores passwords<a class=\"heading-anchor\" href=\"#how-django-stores-passwords\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>Django provides a flexible password storage system and uses PBKDF2 by default.</p>\n<p>The <a class=\"reference internal\" href=\"/es/1.10/ref/contrib/auth/#django.contrib.auth.models.User.password\" title=\"django.contrib.auth.models.User.password\"><code class=\"xref py py-attr docutils literal notranslate\"><span class=\"pre\">password</span></code></a> attribute of a\n<a class=\"reference internal\" href=\"/es/1.10/ref/contrib/auth/#id1\" title=\"django.contrib.auth.models.User\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">User</span></code></a> object is a string in this format:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code>&lt;algorithm&gt;$&lt;iterations&gt;$&lt;salt&gt;$&lt;hash&gt;\n</code></pre></div>\n<p>Those are the components used for storing a User’s password, separated by the\ndollar-sign character and consist of: the hashing algorithm, the number of\nalgorithm iterations (work factor), the random salt, and the resulting password\nhash.  The algorithm is one of a number of one-way hashing or password storage\nalgorithms Django can use; see below. Iterations describe the number of times\nthe algorithm is run over the hash. Salt is the random seed used and the hash\nis the result of the one-way function.</p>\n<p>By default, Django uses the <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/PBKDF2\">PBKDF2</a> algorithm with a SHA256 hash, a\npassword stretching mechanism recommended by <a class=\"reference external\" href=\"https://dx.doi.org/10.6028/NIST.SP.800-132\">NIST</a>. This should be\nsufficient for most users: it’s quite secure, requiring massive\namounts of computing time to break.</p>\n<p>However, depending on your requirements, you may choose a different\nalgorithm, or even use a custom algorithm to match your specific\nsecurity situation. Again, most users shouldn’t need to do this – if\nyou’re not sure, you probably don’t.  If you do, please read on:</p>\n<p>Django chooses the algorithm to use by consulting the\n<a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> setting. This is a list of hashing algorithm\nclasses that this Django installation supports. The first entry in this list\n(that is, <code class=\"docutils literal notranslate\"><span class=\"pre\">settings.PASSWORD_HASHERS[0]</span></code>) will be used to store passwords,\nand all the other entries are valid hashers that can be used to check existing\npasswords.  This means that if you want to use a different algorithm, you’ll\nneed to modify <a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> to list your preferred algorithm\nfirst in the list.</p>\n<p>The default for <a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> is:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">PASSWORD_HASHERS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.PBKDF2PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.Argon2PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.BCryptSHA256PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.BCryptPasswordHasher&#39;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n<p>This means that Django will use <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/PBKDF2\">PBKDF2</a> to store all passwords but will support\nchecking passwords stored with PBKDF2SHA1, <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Argon2\">argon2</a>, and <a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Bcrypt\">bcrypt</a>.</p>\n<p>The next few sections describe a couple of common ways advanced users may want\nto modify this setting.</p>\n<section id=\"using-argon2-with-django\">\n<span id=\"argon2-usage\"></span><h3>Using Argon2 with Django<a class=\"heading-anchor\" href=\"#using-argon2-with-django\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<aside class=\"version-note version-added\" data-version=\"1.10\">\n<p class=\"version-note-title\">New in Django 1.10</p></aside>\n<p><a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Argon2\">Argon2</a> is the winner of the 2015 <a class=\"reference external\" href=\"https://password-hashing.net\">Password Hashing Competition</a>, a community\norganized open competition to select a next generation hashing algorithm. It’s\ndesigned not to be easier to compute on custom hardware than it is to compute\non an ordinary CPU.</p>\n<p><a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Argon2\">Argon2</a> is not the default for Django because it requires a third-party\nlibrary. The Password Hashing Competition panel, however, recommends immediate\nuse of Argon2 rather than the other algorithms supported by Django.</p>\n<p>To use Argon2 as your default storage algorithm, do the following:</p>\n<ol class=\"arabic\">\n<li><p>Install the <a class=\"reference external\" href=\"https://pypi.python.org/pypi/argon2_cffi/\">argon2-cffi library</a>.  This can be done by running <code class=\"docutils literal notranslate\"><span class=\"pre\">pip</span>\n<span class=\"pre\">install</span> <span class=\"pre\">django[argon2]</span></code>, which is equivalent to <code class=\"docutils literal notranslate\"><span class=\"pre\">pip</span> <span class=\"pre\">install</span> <span class=\"pre\">argon2-cffi</span></code>\n(along with any version requirement from Django’s <code class=\"docutils literal notranslate\"><span class=\"pre\">setup.py</span></code>).</p></li>\n<li><p>Modify <a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> to list <code class=\"docutils literal notranslate\"><span class=\"pre\">Argon2PasswordHasher</span></code> first.\nThat is, in your settings file, you’d put:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">PASSWORD_HASHERS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.Argon2PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.PBKDF2PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.BCryptSHA256PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.BCryptPasswordHasher&#39;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n<p>Keep and/or add any entries in this list if you need Django to <a class=\"reference internal\" href=\"#password-upgrades\"><span class=\"std std-ref\">upgrade\npasswords</span></a>.</p>\n</li>\n</ol>\n</section>\n<section id=\"using-bcrypt-with-django\">\n<span id=\"bcrypt-usage\"></span><h3>Using <code class=\"docutils literal notranslate\"><span class=\"pre\">bcrypt</span></code> with Django<a class=\"heading-anchor\" href=\"#using-bcrypt-with-django\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p><a class=\"reference external\" href=\"https://en.wikipedia.org/wiki/Bcrypt\">Bcrypt</a> is a popular password storage algorithm that’s specifically designed\nfor long-term password storage. It’s not the default used by Django since it\nrequires the use of third-party libraries, but since many people may want to\nuse it Django supports bcrypt with minimal effort.</p>\n<p>To use Bcrypt as your default storage algorithm, do the following:</p>\n<ol class=\"arabic\">\n<li><p>Install the <a class=\"reference external\" href=\"https://pypi.python.org/pypi/bcrypt/\">bcrypt library</a>. This can be done by running <code class=\"docutils literal notranslate\"><span class=\"pre\">pip</span> <span class=\"pre\">install</span>\n<span class=\"pre\">django[bcrypt]</span></code>, which is equivalent to  <code class=\"docutils literal notranslate\"><span class=\"pre\">pip</span> <span class=\"pre\">install</span> <span class=\"pre\">bcrypt</span></code> (along with\nany version requirement from Django’s <code class=\"docutils literal notranslate\"><span class=\"pre\">setup.py</span></code>).</p></li>\n<li><p>Modify <a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> to list <code class=\"docutils literal notranslate\"><span class=\"pre\">BCryptSHA256PasswordHasher</span></code>\nfirst. That is, in your settings file, you’d put:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">PASSWORD_HASHERS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.BCryptSHA256PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.BCryptPasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.PBKDF2PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.Argon2PasswordHasher&#39;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n<p>Keep and/or add any entries in this list if you need Django to <a class=\"reference internal\" href=\"#password-upgrades\"><span class=\"std std-ref\">upgrade\npasswords</span></a>.</p>\n</li>\n</ol>\n<p>That’s it – now your Django install will use Bcrypt as the default storage\nalgorithm.</p>\n<aside class=\"admonition-password-truncation-with-bcryptpasswordhasher admonition\">\n<p class=\"admonition-title\">Password truncation with BCryptPasswordHasher</p>\n<p>The designers of bcrypt truncate all passwords at 72 characters which means\nthat <code class=\"docutils literal notranslate\"><span class=\"pre\">bcrypt(password_with_100_chars)</span> <span class=\"pre\">==</span> <span class=\"pre\">bcrypt(password_with_100_chars[:72])</span></code>.\nThe original <code class=\"docutils literal notranslate\"><span class=\"pre\">BCryptPasswordHasher</span></code> does not have any special handling and\nthus is also subject to this hidden password length limit.\n<code class=\"docutils literal notranslate\"><span class=\"pre\">BCryptSHA256PasswordHasher</span></code> fixes this by first hashing the\npassword using sha256. This prevents the password truncation and so should\nbe preferred over the <code class=\"docutils literal notranslate\"><span class=\"pre\">BCryptPasswordHasher</span></code>. The practical ramification\nof this truncation is pretty marginal as the average user does not have a\npassword greater than 72 characters in length and even being truncated at 72\nthe compute powered required to brute force bcrypt in any useful amount of\ntime is still astronomical. Nonetheless, we recommend you use\n<code class=\"docutils literal notranslate\"><span class=\"pre\">BCryptSHA256PasswordHasher</span></code> anyway on the principle of «better safe than\nsorry».</p>\n</aside>\n<aside class=\"admonition-other-bcrypt-implementations admonition\">\n<p class=\"admonition-title\">Other bcrypt implementations</p>\n<p>There are several other implementations that allow bcrypt to be\nused with Django. Django’s bcrypt support is NOT directly\ncompatible with these. To upgrade, you will need to modify the\nhashes in your database to be in the form <code class=\"docutils literal notranslate\"><span class=\"pre\">bcrypt$(raw</span> <span class=\"pre\">bcrypt</span>\n<span class=\"pre\">output)</span></code>. For example:\n<code class=\"docutils literal notranslate\"><span class=\"pre\">bcrypt$$2a$12$NT0I31Sa7ihGEWpka9ASYrEFkhuTNeBQ2xfZskIiiJeyFXhRgS.Sy</span></code>.</p>\n</aside>\n</section>\n<section id=\"increasing-the-work-factor\">\n<span id=\"increasing-password-algorithm-work-factor\"></span><h3>Increasing the work factor<a class=\"heading-anchor\" href=\"#increasing-the-work-factor\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<section id=\"pbkdf2-and-bcrypt\">\n<h4>PBKDF2 and bcrypt<a class=\"heading-anchor\" href=\"#pbkdf2-and-bcrypt\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>The PBKDF2 and bcrypt algorithms use a number of iterations or rounds of\nhashing. This deliberately slows down attackers, making attacks against hashed\npasswords harder. However, as computing power increases, the number of\niterations needs to be increased. We’ve chosen a reasonable default (and will\nincrease it with each release of Django), but you may wish to tune it up or\ndown, depending on your security needs and available processing power. To do so,\nyou’ll subclass the appropriate algorithm and override the <code class=\"docutils literal notranslate\"><span class=\"pre\">iterations</span></code>\nparameters. For example, to increase the number of iterations used by the\ndefault PBKDF2 algorithm:</p>\n<ol class=\"arabic\">\n<li><p>Create a subclass of <code class=\"docutils literal notranslate\"><span class=\"pre\">django.contrib.auth.hashers.PBKDF2PasswordHasher</span></code>:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.contrib.auth.hashers</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">PBKDF2PasswordHasher</span>\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MyPBKDF2PasswordHasher</span><span class=\"p\">(</span><span class=\"n\">PBKDF2PasswordHasher</span><span class=\"p\">):</span>\n<span class=\"w\">    </span><span class=\"sd\">&quot;&quot;&quot;</span>\n<span class=\"sd\">    A subclass of PBKDF2PasswordHasher that uses 100 times more iterations.</span>\n<span class=\"sd\">    &quot;&quot;&quot;</span>\n    <span class=\"n\">iterations</span> <span class=\"o\">=</span> <span class=\"n\">PBKDF2PasswordHasher</span><span class=\"o\">.</span><span class=\"n\">iterations</span> <span class=\"o\">*</span> <span class=\"mi\">100</span>\n</code></pre></div>\n<p>Save this somewhere in your project. For example, you might put this in\na file like <code class=\"docutils literal notranslate\"><span class=\"pre\">myproject/hashers.py</span></code>.</p>\n</li>\n<li><p>Add your new hasher as the first entry in <a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a>:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">PASSWORD_HASHERS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"s1\">&#39;myproject.hashers.MyPBKDF2PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.PBKDF2PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.Argon2PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.BCryptSHA256PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.BCryptPasswordHasher&#39;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n</li>\n</ol>\n<p>That’s it – now your Django install will use more iterations when it\nstores passwords using PBKDF2.</p>\n</section>\n<section id=\"argon2\">\n<h4>Argon2<a class=\"heading-anchor\" href=\"#argon2\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h4>\n<p>Argon2 has three attributes that can be customized:</p>\n<ol class=\"arabic simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">time_cost</span></code> controls the number of iterations within the hash.</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">memory_cost</span></code> controls the size of memory that must be used during the\ncomputation of the hash.</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">parallelism</span></code> controls how many CPUs the computation of the hash can be\nparallelized on.</p></li>\n</ol>\n<p>The default values of these attributes are probably fine for you. If you\ndetermine that the password hash is too fast or too slow, you can tweak it as\nfollows:</p>\n<ol class=\"arabic simple\">\n<li><p>Choose <code class=\"docutils literal notranslate\"><span class=\"pre\">parallelism</span></code> to be the number of threads you can\nspare computing the hash.</p></li>\n<li><p>Choose <code class=\"docutils literal notranslate\"><span class=\"pre\">memory_cost</span></code> to be the KiB of memory you can spare.</p></li>\n<li><p>Adjust <code class=\"docutils literal notranslate\"><span class=\"pre\">time_cost</span></code> and measure the time hashing a password takes.\nPick a <code class=\"docutils literal notranslate\"><span class=\"pre\">time_cost</span></code> that takes an acceptable time for you.\nIf <code class=\"docutils literal notranslate\"><span class=\"pre\">time_cost</span></code> set to 1 is unacceptably slow, lower <code class=\"docutils literal notranslate\"><span class=\"pre\">memory_cost</span></code>.</p></li>\n</ol>\n<aside class=\"admonition-memory-cost-interpretation admonition\">\n<p class=\"admonition-title\"><code class=\"docutils literal notranslate\"><span class=\"pre\">memory_cost</span></code> interpretation</p>\n<p>The argon2 command-line utility and some other libraries interpret the\n<code class=\"docutils literal notranslate\"><span class=\"pre\">memory_cost</span></code> parameter differently from the value that Django uses. The\nconversion is given by <code class=\"docutils literal notranslate\"><span class=\"pre\">memory_cost</span> <span class=\"pre\">==</span> <span class=\"pre\">2</span> <span class=\"pre\">**</span> <span class=\"pre\">memory_cost_commandline</span></code>.</p>\n</aside>\n</section>\n</section>\n<section id=\"password-upgrading\">\n<span id=\"password-upgrades\"></span><h3>Password upgrading<a class=\"heading-anchor\" href=\"#password-upgrading\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>When users log in, if their passwords are stored with anything other than\nthe preferred algorithm, Django will automatically upgrade the algorithm\nto the preferred one. This means that old installs of Django will get\nautomatically more secure as users log in, and it also means that you\ncan switch to new (and better) storage algorithms as they get invented.</p>\n<p>However, Django can only upgrade passwords that use algorithms mentioned in\n<a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a>, so as you upgrade to new systems you should make\nsure never to <em>remove</em> entries from this list. If you do, users using\nunmentioned algorithms won’t be able to upgrade. Hashed passwords will be\nupdated when increasing (or decreasing) the number of PBKDF2 iterations or\nbcrypt rounds.</p>\n<p>Be aware that if all the passwords in your database aren’t encoded in the\ndefault hasher’s algorithm, you may be vulnerable to a user enumeration timing\nattack due to a difference between the duration of a login request for a user\nwith a password encoded in a non-default algorithm and the duration of a login\nrequest for a nonexistent user (which runs the default hasher). You may be able\nto mitigate this by <a class=\"reference internal\" href=\"#wrapping-password-hashers\"><span class=\"std std-ref\">upgrading older password hashes</span></a>.</p>\n<aside class=\"version-note version-changed\" data-version=\"1.9\">\n<p class=\"version-note-title\">Changed in Django 1.9</p><p>Passwords updates when changing the number of bcrypt rounds was added.</p>\n</aside>\n</section>\n<section id=\"password-upgrading-without-requiring-a-login\">\n<span id=\"wrapping-password-hashers\"></span><h3>Password upgrading without requiring a login<a class=\"heading-anchor\" href=\"#password-upgrading-without-requiring-a-login\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>If you have an existing database with an older, weak hash such as MD5 or SHA1,\nyou might want to upgrade those hashes yourself instead of waiting for the\nupgrade to happen when a user logs in (which may never happen if a user doesn’t\nreturn to your site). In this case, you can use a «wrapped» password hasher.</p>\n<p>For this example, we’ll migrate a collection of SHA1 hashes to use\nPBKDF2(SHA1(password)) and add the corresponding password hasher for checking\nif a user entered the correct password on login. We assume we’re using the\nbuilt-in <code class=\"docutils literal notranslate\"><span class=\"pre\">User</span></code> model and that our project has an <code class=\"docutils literal notranslate\"><span class=\"pre\">accounts</span></code> app. You can\nmodify the pattern to work with any algorithm or with a custom user model.</p>\n<p>First, we’ll add the custom hasher:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>accounts/hashers.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"accounts/hashers.py\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.contrib.auth.hashers</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"p\">(</span>\n    <span class=\"n\">PBKDF2PasswordHasher</span><span class=\"p\">,</span> <span class=\"n\">SHA1PasswordHasher</span><span class=\"p\">,</span>\n<span class=\"p\">)</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">PBKDF2WrappedSHA1PasswordHasher</span><span class=\"p\">(</span><span class=\"n\">PBKDF2PasswordHasher</span><span class=\"p\">):</span>\n    <span class=\"n\">algorithm</span> <span class=\"o\">=</span> <span class=\"s1\">&#39;pbkdf2_wrapped_sha1&#39;</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">encode_sha1_hash</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">sha1_hash</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">,</span> <span class=\"n\">iterations</span><span class=\"o\">=</span><span class=\"kc\">None</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"nb\">super</span><span class=\"p\">(</span><span class=\"n\">PBKDF2WrappedSHA1PasswordHasher</span><span class=\"p\">,</span> <span class=\"bp\">self</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">encode</span><span class=\"p\">(</span><span class=\"n\">sha1_hash</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">,</span> <span class=\"n\">iterations</span><span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">encode</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">password</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">,</span> <span class=\"n\">iterations</span><span class=\"o\">=</span><span class=\"kc\">None</span><span class=\"p\">):</span>\n        <span class=\"n\">_</span><span class=\"p\">,</span> <span class=\"n\">_</span><span class=\"p\">,</span> <span class=\"n\">sha1_hash</span> <span class=\"o\">=</span> <span class=\"n\">SHA1PasswordHasher</span><span class=\"p\">()</span><span class=\"o\">.</span><span class=\"n\">encode</span><span class=\"p\">(</span><span class=\"n\">password</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">)</span><span class=\"o\">.</span><span class=\"n\">split</span><span class=\"p\">(</span><span class=\"s1\">&#39;$&#39;</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">)</span>\n        <span class=\"k\">return</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">encode_sha1_hash</span><span class=\"p\">(</span><span class=\"n\">sha1_hash</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">,</span> <span class=\"n\">iterations</span><span class=\"p\">)</span>\n</code></pre></figure>\n<p>The data migration might look something like:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>accounts/migrations/0002_migrate_sha1_passwords.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"accounts/migrations/0002_migrate_sha1_passwords.py\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.db</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">migrations</span>\n\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">..hashers</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">PBKDF2WrappedSHA1PasswordHasher</span>\n\n\n<span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">forwards_func</span><span class=\"p\">(</span><span class=\"n\">apps</span><span class=\"p\">,</span> <span class=\"n\">schema_editor</span><span class=\"p\">):</span>\n    <span class=\"n\">User</span> <span class=\"o\">=</span> <span class=\"n\">apps</span><span class=\"o\">.</span><span class=\"n\">get_model</span><span class=\"p\">(</span><span class=\"s1\">&#39;auth&#39;</span><span class=\"p\">,</span> <span class=\"s1\">&#39;User&#39;</span><span class=\"p\">)</span>\n    <span class=\"n\">users</span> <span class=\"o\">=</span> <span class=\"n\">User</span><span class=\"o\">.</span><span class=\"n\">objects</span><span class=\"o\">.</span><span class=\"n\">filter</span><span class=\"p\">(</span><span class=\"n\">password__startswith</span><span class=\"o\">=</span><span class=\"s1\">&#39;sha1$&#39;</span><span class=\"p\">)</span>\n    <span class=\"n\">hasher</span> <span class=\"o\">=</span> <span class=\"n\">PBKDF2WrappedSHA1PasswordHasher</span><span class=\"p\">()</span>\n    <span class=\"k\">for</span> <span class=\"n\">user</span> <span class=\"ow\">in</span> <span class=\"n\">users</span><span class=\"p\">:</span>\n        <span class=\"n\">algorithm</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">,</span> <span class=\"n\">sha1_hash</span> <span class=\"o\">=</span> <span class=\"n\">user</span><span class=\"o\">.</span><span class=\"n\">password</span><span class=\"o\">.</span><span class=\"n\">split</span><span class=\"p\">(</span><span class=\"s1\">&#39;$&#39;</span><span class=\"p\">,</span> <span class=\"mi\">2</span><span class=\"p\">)</span>\n        <span class=\"n\">user</span><span class=\"o\">.</span><span class=\"n\">password</span> <span class=\"o\">=</span> <span class=\"n\">hasher</span><span class=\"o\">.</span><span class=\"n\">encode_sha1_hash</span><span class=\"p\">(</span><span class=\"n\">sha1_hash</span><span class=\"p\">,</span> <span class=\"n\">salt</span><span class=\"p\">)</span>\n        <span class=\"n\">user</span><span class=\"o\">.</span><span class=\"n\">save</span><span class=\"p\">(</span><span class=\"n\">update_fields</span><span class=\"o\">=</span><span class=\"p\">[</span><span class=\"s1\">&#39;password&#39;</span><span class=\"p\">])</span>\n\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">Migration</span><span class=\"p\">(</span><span class=\"n\">migrations</span><span class=\"o\">.</span><span class=\"n\">Migration</span><span class=\"p\">):</span>\n\n    <span class=\"n\">dependencies</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n        <span class=\"p\">(</span><span class=\"s1\">&#39;accounts&#39;</span><span class=\"p\">,</span> <span class=\"s1\">&#39;0001_initial&#39;</span><span class=\"p\">),</span>\n        <span class=\"c1\"># replace this with the latest migration in contrib.auth</span>\n        <span class=\"p\">(</span><span class=\"s1\">&#39;auth&#39;</span><span class=\"p\">,</span> <span class=\"s1\">&#39;####_migration_name&#39;</span><span class=\"p\">),</span>\n    <span class=\"p\">]</span>\n\n    <span class=\"n\">operations</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n        <span class=\"n\">migrations</span><span class=\"o\">.</span><span class=\"n\">RunPython</span><span class=\"p\">(</span><span class=\"n\">forwards_func</span><span class=\"p\">),</span>\n    <span class=\"p\">]</span>\n</code></pre></figure>\n<p>Be aware that this migration will take on the order of several minutes for\nseveral thousand users, depending on the speed of your hardware.</p>\n<p>Finally, we’ll add a <a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-PASSWORD_HASHERS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code></a> setting:</p>\n<figure class=\"code-block code-block-captioned\" data-language=\"default\"><figcaption class=\"code-block-caption\"><code>mysite/settings.py</code></figcaption><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"mysite/settings.py\"><code><span class=\"n\">PASSWORD_HASHERS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.PBKDF2PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;accounts.hashers.PBKDF2WrappedSHA1PasswordHasher&#39;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></figure>\n<p>Include any other hashers that your site uses in this list.</p>\n</section>\n<section id=\"included-hashers\">\n<span id=\"auth-included-hashers\"></span><h3>Included hashers<a class=\"heading-anchor\" href=\"#included-hashers\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>The full list of hashers included in Django is:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"p\">[</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.PBKDF2PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.PBKDF2SHA1PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.Argon2PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.BCryptSHA256PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.BCryptPasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.SHA1PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.MD5PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.UnsaltedSHA1PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.UnsaltedMD5PasswordHasher&#39;</span><span class=\"p\">,</span>\n    <span class=\"s1\">&#39;django.contrib.auth.hashers.CryptPasswordHasher&#39;</span><span class=\"p\">,</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n<p>The corresponding algorithm names are:</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">pbkdf2_sha256</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">pbkdf2_sha1</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">argon2</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">bcrypt_sha256</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">bcrypt</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">sha1</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">md5</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">unsalted_sha1</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">unsalted_md5</span></code></p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">crypt</span></code></p></li>\n</ul>\n</section>\n<section id=\"writing-your-own-hasher\">\n<span id=\"write-your-own-password-hasher\"></span><h3>Writing your own hasher<a class=\"heading-anchor\" href=\"#writing-your-own-hasher\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<aside class=\"version-note version-added\" data-version=\"1.9.3\">\n<p class=\"version-note-title\">New in Django 1.9.3</p></aside>\n<p>If you write your own password hasher that contains a work factor such as a\nnumber of iterations, you should implement a\n<code class=\"docutils literal notranslate\"><span class=\"pre\">harden_runtime(self,</span> <span class=\"pre\">password,</span> <span class=\"pre\">encoded)</span></code> method to bridge the runtime gap\nbetween the work factor supplied in the <code class=\"docutils literal notranslate\"><span class=\"pre\">encoded</span></code> password and the default\nwork factor of the hasher. This prevents a user enumeration timing attack due\nto  difference between a login request for a user with a password encoded in an\nolder number of iterations and a nonexistent user (which runs the default\nhasher’s default number of iterations).</p>\n<p>Taking PBKDF2 as example, if <code class=\"docutils literal notranslate\"><span class=\"pre\">encoded</span></code> contains 20,000 iterations and the\nhasher’s default <code class=\"docutils literal notranslate\"><span class=\"pre\">iterations</span></code> is 30,000, the method should run <code class=\"docutils literal notranslate\"><span class=\"pre\">password</span></code>\nthrough another 10,000 iterations of PBKDF2.</p>\n<p>If your hasher doesn’t have a work factor, implement the method as a no-op\n(<code class=\"docutils literal notranslate\"><span class=\"pre\">pass</span></code>).</p>\n</section>\n</section>\n<section id=\"module-django.contrib.auth.hashers\">\n<span id=\"manually-managing-a-user-s-password\"></span><h2>Manually managing a user’s password<a class=\"heading-anchor\" href=\"#module-django.contrib.auth.hashers\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<p>The <a class=\"reference internal\" href=\"#module-django.contrib.auth.hashers\" title=\"django.contrib.auth.hashers\"><code class=\"xref py py-mod docutils literal notranslate\"><span class=\"pre\">django.contrib.auth.hashers</span></code></a> module provides a set of functions\nto create and validate hashed password. You can use them independently\nfrom the <code class=\"docutils literal notranslate\"><span class=\"pre\">User</span></code> model.</p>\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.hashers.check_password\">\n<span class=\"sig-name descname\"><span class=\"pre\">check_password</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">encoded</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.hashers.check_password\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>If you’d like to manually authenticate a user by comparing a plain-text\npassword to the hashed password in the database, use the convenience\nfunction <a class=\"reference internal\" href=\"#django.contrib.auth.hashers.check_password\" title=\"django.contrib.auth.hashers.check_password\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">check_password()</span></code></a>. It takes two arguments: the plain-text\npassword to check, and the full value of a user’s <code class=\"docutils literal notranslate\"><span class=\"pre\">password</span></code> field in the\ndatabase to check against, and returns <code class=\"docutils literal notranslate\"><span class=\"pre\">True</span></code> if they match, <code class=\"docutils literal notranslate\"><span class=\"pre\">False</span></code>\notherwise.</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.hashers.make_password\">\n<span class=\"sig-name descname\"><span class=\"pre\">make_password</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">salt</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">hasher</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">'default'</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.hashers.make_password\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Creates a hashed password in the format used by this application. It takes\none mandatory argument: the password in plain-text. Optionally, you can\nprovide a salt and a hashing algorithm to use, if you don’t want to use the\ndefaults (first entry of <code class=\"docutils literal notranslate\"><span class=\"pre\">PASSWORD_HASHERS</span></code> setting). See\n<a class=\"reference internal\" href=\"#auth-included-hashers\"><span class=\"std std-ref\">Included hashers</span></a> for the algorithm name of each hasher. If the\npassword argument is <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code>, an unusable password is returned (a one that\nwill be never accepted by <a class=\"reference internal\" href=\"#django.contrib.auth.hashers.check_password\" title=\"django.contrib.auth.hashers.check_password\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">check_password()</span></code></a>).</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.hashers.is_password_usable\">\n<span class=\"sig-name descname\"><span class=\"pre\">is_password_usable</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">encoded_password</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.hashers.is_password_usable\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Checks if the given string is a hashed password that has a chance\nof being verified against <a class=\"reference internal\" href=\"#django.contrib.auth.hashers.check_password\" title=\"django.contrib.auth.hashers.check_password\"><code class=\"xref py py-func docutils literal notranslate\"><span class=\"pre\">check_password()</span></code></a>.</p>\n</dd></dl>\n\n</section>\n<section id=\"module-django.contrib.auth.password_validation\">\n<span id=\"id2\"></span><span id=\"password-validation\"></span><h2>Password validation<a class=\"heading-anchor\" href=\"#module-django.contrib.auth.password_validation\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h2>\n<aside class=\"version-note version-added\" data-version=\"1.9\">\n<p class=\"version-note-title\">New in Django 1.9</p></aside>\n<p>Users often choose poor passwords. To help mitigate this problem, Django\noffers pluggable password validation. You can configure multiple password\nvalidators at the same time. A few validators are included in Django, but it’s\nsimple to write your own as well.</p>\n<p>Each password validator must provide a help text to explain the requirements to\nthe user, validate a given password and return an error message if it does not\nmeet the requirements, and optionally receive passwords that have been set.\nValidators can also have optional settings to fine tune their behavior.</p>\n<p>Validation is controlled by the <a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a> setting.\nThe default for the setting is an empty list, which means no validators are\napplied. In new projects created with the default <a class=\"reference internal\" href=\"/es/1.10/ref/django-admin/#django-admin-startproject\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">startproject</span></code></a>\ntemplate, a simple set of validators is enabled.</p>\n<p>By default, validators are used in the forms to reset or change passwords and\nin the <a class=\"reference internal\" href=\"/es/1.10/ref/django-admin/#django-admin-createsuperuser\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">createsuperuser</span></code></a> and <a class=\"reference internal\" href=\"/es/1.10/ref/django-admin/#django-admin-changepassword\"><code class=\"xref std std-djadmin docutils literal notranslate\"><span class=\"pre\">changepassword</span></code></a> management\ncommands. Validators aren’t applied at the model level, for example in\n<code class=\"docutils literal notranslate\"><span class=\"pre\">User.objects.create_user()</span></code> and <code class=\"docutils literal notranslate\"><span class=\"pre\">create_superuser()</span></code>, because we assume\nthat developers, not users, interact with Django at that level and also because\nmodel validation doesn’t automatically run as part of creating models.</p>\n<aside class=\"admonition admonition-note\" role=\"note\">\n<p class=\"admonition-title\">Nota</p>\n<p>Password validation can prevent the use of many types of weak passwords.\nHowever, the fact that a password passes all the validators doesn’t\nguarantee that it is a strong password. There are many factors that can\nweaken a password that are not detectable by even the most advanced\npassword validators.</p>\n</aside>\n<section id=\"enabling-password-validation\">\n<h3>Enabling password validation<a class=\"heading-anchor\" href=\"#enabling-password-validation\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Password validation is configured in the\n<a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a> setting:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"n\">AUTH_PASSWORD_VALIDATORS</span> <span class=\"o\">=</span> <span class=\"p\">[</span>\n    <span class=\"p\">{</span>\n        <span class=\"s1\">&#39;NAME&#39;</span><span class=\"p\">:</span> <span class=\"s1\">&#39;django.contrib.auth.password_validation.UserAttributeSimilarityValidator&#39;</span><span class=\"p\">,</span>\n    <span class=\"p\">},</span>\n    <span class=\"p\">{</span>\n        <span class=\"s1\">&#39;NAME&#39;</span><span class=\"p\">:</span> <span class=\"s1\">&#39;django.contrib.auth.password_validation.MinimumLengthValidator&#39;</span><span class=\"p\">,</span>\n        <span class=\"s1\">&#39;OPTIONS&#39;</span><span class=\"p\">:</span> <span class=\"p\">{</span>\n            <span class=\"s1\">&#39;min_length&#39;</span><span class=\"p\">:</span> <span class=\"mi\">9</span><span class=\"p\">,</span>\n        <span class=\"p\">}</span>\n    <span class=\"p\">},</span>\n    <span class=\"p\">{</span>\n        <span class=\"s1\">&#39;NAME&#39;</span><span class=\"p\">:</span> <span class=\"s1\">&#39;django.contrib.auth.password_validation.CommonPasswordValidator&#39;</span><span class=\"p\">,</span>\n    <span class=\"p\">},</span>\n    <span class=\"p\">{</span>\n        <span class=\"s1\">&#39;NAME&#39;</span><span class=\"p\">:</span> <span class=\"s1\">&#39;django.contrib.auth.password_validation.NumericPasswordValidator&#39;</span><span class=\"p\">,</span>\n    <span class=\"p\">},</span>\n<span class=\"p\">]</span>\n</code></pre></div>\n<p>This example enables all four included validators:</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">UserAttributeSimilarityValidator</span></code>, which checks the similarity between\nthe password and a set of attributes of the user.</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">MinimumLengthValidator</span></code>, which simply checks whether the password meets a\nminimum length. This validator is configured with a custom option: it now\nrequires the minimum length to be nine characters, instead of the default\neight.</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">CommonPasswordValidator</span></code>, which checks whether the password occurs in a\nlist of common passwords. By default, it compares to an included list of\n1000 common passwords.</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">NumericPasswordValidator</span></code>, which checks whether the password isn’t\nentirely numeric.</p></li>\n</ul>\n<p>For <code class=\"docutils literal notranslate\"><span class=\"pre\">UserAttributeSimilarityValidator</span></code> and <code class=\"docutils literal notranslate\"><span class=\"pre\">CommonPasswordValidator</span></code>,\nwe’re simply using the default settings in this example.\n<code class=\"docutils literal notranslate\"><span class=\"pre\">NumericPasswordValidator</span></code> has no settings.</p>\n<p>The help texts and any errors from password validators are always returned in\nthe order they are listed in <a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a>.</p>\n</section>\n<section id=\"included-validators\">\n<h3>Included validators<a class=\"heading-anchor\" href=\"#included-validators\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>Django includes four validators:</p>\n<dl class=\"py class\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.MinimumLengthValidator\">\n<em class=\"property\"><span class=\"k\"><span class=\"pre\">class</span></span><span class=\"w\"> </span></em><span class=\"sig-name descname\"><span class=\"pre\">MinimumLengthValidator</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">min_length</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">8</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.MinimumLengthValidator\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Validates whether the password meets a minimum length.\nThe minimum length can be customized with the <code class=\"docutils literal notranslate\"><span class=\"pre\">min_length</span></code> parameter.</p>\n</dd></dl>\n\n<dl class=\"py class\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.UserAttributeSimilarityValidator\">\n<em class=\"property\"><span class=\"k\"><span class=\"pre\">class</span></span><span class=\"w\"> </span></em><span class=\"sig-name descname\"><span class=\"pre\">UserAttributeSimilarityValidator</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">user_attributes</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">DEFAULT_USER_ATTRIBUTES</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">max_similarity</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">0.7</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.UserAttributeSimilarityValidator\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Validates whether the password is sufficiently different from certain\nattributes of the user.</p>\n<p>The <code class=\"docutils literal notranslate\"><span class=\"pre\">user_attributes</span></code> parameter should be an iterable of names of user\nattributes to compare to. If this argument is not provided, the default\nis used: <code class=\"docutils literal notranslate\"><span class=\"pre\">'username',</span> <span class=\"pre\">'first_name',</span> <span class=\"pre\">'last_name',</span> <span class=\"pre\">'email'</span></code>.\nAttributes that don’t exist are ignored.</p>\n<p>The maximum similarity the password can have, before it is rejected, can\nbe set with the <code class=\"docutils literal notranslate\"><span class=\"pre\">max_similarity</span></code> parameter, on a scale of 0 to 1.\nA setting of 0 will cause all passwords to be rejected, whereas a setting\nof 1 will cause it to only reject passwords that are identical to an\nattribute’s value.</p>\n</dd></dl>\n\n<dl class=\"py class\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.CommonPasswordValidator\">\n<em class=\"property\"><span class=\"k\"><span class=\"pre\">class</span></span><span class=\"w\"> </span></em><span class=\"sig-name descname\"><span class=\"pre\">CommonPasswordValidator</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password_list_path</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">DEFAULT_PASSWORD_LIST_PATH</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.CommonPasswordValidator\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Validates whether the password is not a common password. By default, this\nchecks against a list of 1000 common password created by\n<a class=\"reference external\" href=\"https://web.archive.org/web/20150315154609/https://xato.net/passwords/more-top-worst-passwords/\">Mark Burnett</a>.</p>\n<p>The <code class=\"docutils literal notranslate\"><span class=\"pre\">password_list_path</span></code> can be set to the path of a custom file of\ncommon passwords. This file should contain one password per line and\nmay be plain text or gzipped.</p>\n</dd></dl>\n\n<dl class=\"py class\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.NumericPasswordValidator\">\n<em class=\"property\"><span class=\"k\"><span class=\"pre\">class</span></span><span class=\"w\"> </span></em><span class=\"sig-name descname\"><span class=\"pre\">NumericPasswordValidator</span></span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.NumericPasswordValidator\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Validates whether the password is not entirely numeric.</p>\n</dd></dl>\n\n</section>\n<section id=\"integrating-validation\">\n<h3>Integrating validation<a class=\"heading-anchor\" href=\"#integrating-validation\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>There are a few functions in <code class=\"docutils literal notranslate\"><span class=\"pre\">django.contrib.auth.password_validation</span></code> that\nyou can call from your own forms or other code to integrate password\nvalidation. This can be useful if you use custom forms for password setting,\nor if you have API calls that allow passwords to be set, for example.</p>\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.validate_password\">\n<span class=\"sig-name descname\"><span class=\"pre\">validate_password</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">user</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password_validators</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.validate_password\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Validates a password. If all validators find the password valid, returns\n<code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code>. If one or more validators reject the password, raises a\n<a class=\"reference internal\" href=\"/es/1.10/ref/exceptions/#django.core.exceptions.ValidationError\" title=\"django.core.exceptions.ValidationError\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">ValidationError</span></code></a> with all the error messages\nfrom the validators.</p>\n<p>The <code class=\"docutils literal notranslate\"><span class=\"pre\">user</span></code> object is optional: if it’s not provided, some validators may\nnot be able to perform any validation and will accept any password.</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.password_changed\">\n<span class=\"sig-name descname\"><span class=\"pre\">password_changed</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">user</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em>, <em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password_validators</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.password_changed\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Informs all validators that the password has been changed. This can be used\nby validators such as one that prevents password reuse. This should be\ncalled once the password has been successfully changed.</p>\n<p>For subclasses of <a class=\"reference internal\" href=\"/es/1.10/topics/auth/customizing/#django.contrib.auth.models.AbstractBaseUser\" title=\"django.contrib.auth.models.AbstractBaseUser\"><code class=\"xref py py-class docutils literal notranslate\"><span class=\"pre\">AbstractBaseUser</span></code></a>,\nthe password field will be marked as «dirty» when calling\n<a class=\"reference internal\" href=\"/es/1.10/topics/auth/customizing/#django.contrib.auth.models.AbstractBaseUser.set_password\" title=\"django.contrib.auth.models.AbstractBaseUser.set_password\"><code class=\"xref py py-meth docutils literal notranslate\"><span class=\"pre\">set_password()</span></code></a> which\ntriggers a call to <code class=\"docutils literal notranslate\"><span class=\"pre\">password_changed()</span></code> after the user is saved.</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.password_validators_help_texts\">\n<span class=\"sig-name descname\"><span class=\"pre\">password_validators_help_texts</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password_validators</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.password_validators_help_texts\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Returns a list of the help texts of all validators. These explain the\npassword requirements to the user.</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.password_validators_help_text_html\">\n<span class=\"sig-name descname\"><span class=\"pre\">password_validators_help_text_html</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">password_validators</span></span><span class=\"o\"><span class=\"pre\">=</span></span><span class=\"default_value\"><span class=\"pre\">None</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.password_validators_help_text_html\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Returns an HTML string with all help texts in an <code class=\"docutils literal notranslate\"><span class=\"pre\">&lt;ul&gt;</span></code>. This is\nhelpful when adding password validation to forms, as you can pass the\noutput directly to the <code class=\"docutils literal notranslate\"><span class=\"pre\">help_text</span></code> parameter of a form field.</p>\n</dd></dl>\n\n<dl class=\"py function\">\n<dt class=\"sig sig-object py\" id=\"django.contrib.auth.password_validation.get_password_validators\">\n<span class=\"sig-name descname\"><span class=\"pre\">get_password_validators</span></span><span class=\"sig-paren\">(</span><em class=\"sig-param\"><span class=\"n\"><span class=\"pre\">validator_config</span></span></em><span class=\"sig-paren\">)</span><a class=\"heading-anchor\" href=\"#django.contrib.auth.password_validation.get_password_validators\"><span class=\"visually-hidden\">Link to this definition</span><span aria-hidden=\"true\">#</span></a></dt>\n<dd><p>Returns a set of validator objects based on the <code class=\"docutils literal notranslate\"><span class=\"pre\">validator_config</span></code>\nparameter. By default, all functions use the validators defined in\n<a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a>, but by calling this function with an\nalternate set of validators and then passing the result into the\n<code class=\"docutils literal notranslate\"><span class=\"pre\">password_validators</span></code> parameter of the other functions, your custom set\nof validators will be used instead. This is useful when you have a typical\nset of validators to use for most scenarios, but also have a special\nsituation that requires a custom set. If you always use the same set\nof validators, there is no need to use this function, as the configuration\nfrom <a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a> is used by default.</p>\n<p>The structure of <code class=\"docutils literal notranslate\"><span class=\"pre\">validator_config</span></code> is identical to the\nstructure of <a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a>. The return value of\nthis function can be passed into the <code class=\"docutils literal notranslate\"><span class=\"pre\">password_validators</span></code> parameter\nof the functions listed above.</p>\n</dd></dl>\n\n<p>Note that where the password is passed to one of these functions, this should\nalways be the clear text password - not a hashed password.</p>\n</section>\n<section id=\"writing-your-own-validator\">\n<h3>Writing your own validator<a class=\"heading-anchor\" href=\"#writing-your-own-validator\"><span class=\"visually-hidden\">Link to this heading</span><span aria-hidden=\"true\">#</span></a></h3>\n<p>If Django’s built-in validators are not sufficient, you can write your own\npassword validators. Validators are fairly simple classes. They must implement\ntwo methods:</p>\n<ul class=\"simple\">\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">validate(self,</span> <span class=\"pre\">password,</span> <span class=\"pre\">user=None)</span></code>: validate a password. Return\n<code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code> if the password is valid, or raise a\n<a class=\"reference internal\" href=\"/es/1.10/ref/exceptions/#django.core.exceptions.ValidationError\" title=\"django.core.exceptions.ValidationError\"><code class=\"xref py py-exc docutils literal notranslate\"><span class=\"pre\">ValidationError</span></code></a> with an error message if the\npassword is not valid. You must be able to deal with <code class=\"docutils literal notranslate\"><span class=\"pre\">user</span></code> being\n<code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code> - if that means your validator can’t run, simply return <code class=\"docutils literal notranslate\"><span class=\"pre\">None</span></code>\nfor no error.</p></li>\n<li><p><code class=\"docutils literal notranslate\"><span class=\"pre\">get_help_text()</span></code>: provide a help text to explain the requirements to\nthe user.</p></li>\n</ul>\n<p>Any items in the <code class=\"docutils literal notranslate\"><span class=\"pre\">OPTIONS</span></code> in <a class=\"reference internal\" href=\"/es/1.10/ref/settings/#std-setting-AUTH_PASSWORD_VALIDATORS\"><code class=\"xref std std-setting docutils literal notranslate\"><span class=\"pre\">AUTH_PASSWORD_VALIDATORS</span></code></a> for your\nvalidator will be passed to the constructor. All constructor arguments should\nhave a default value.</p>\n<p>Here’s a basic example of a validator, with one optional setting:</p>\n<div class=\"code-block\" data-language=\"default\"><div class=\"code-block-toolbar\"><span class=\"code-block-language\">Code</span><button type=\"button\" class=\"copy-button\" data-copy hidden><span class=\"copy-button-label\">Copy</span></button></div><pre role=\"group\" tabindex=\"0\" aria-label=\"Code code\"><code><span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.core.exceptions</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">ValidationError</span>\n<span class=\"kn\">from</span><span class=\"w\"> </span><span class=\"nn\">django.utils.translation</span><span class=\"w\"> </span><span class=\"kn\">import</span> <span class=\"n\">ugettext</span> <span class=\"k\">as</span> <span class=\"n\">_</span>\n\n<span class=\"k\">class</span><span class=\"w\"> </span><span class=\"nc\">MinimumLengthValidator</span><span class=\"p\">(</span><span class=\"nb\">object</span><span class=\"p\">):</span>\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"fm\">__init__</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">min_length</span><span class=\"o\">=</span><span class=\"mi\">8</span><span class=\"p\">):</span>\n        <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">min_length</span> <span class=\"o\">=</span> <span class=\"n\">min_length</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">validate</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">,</span> <span class=\"n\">password</span><span class=\"p\">,</span> <span class=\"n\">user</span><span class=\"o\">=</span><span class=\"kc\">None</span><span class=\"p\">):</span>\n        <span class=\"k\">if</span> <span class=\"nb\">len</span><span class=\"p\">(</span><span class=\"n\">password</span><span class=\"p\">)</span> <span class=\"o\">&lt;</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">min_length</span><span class=\"p\">:</span>\n            <span class=\"k\">raise</span> <span class=\"n\">ValidationError</span><span class=\"p\">(</span>\n                <span class=\"n\">_</span><span class=\"p\">(</span><span class=\"s2\">&quot;This password must contain at least </span><span class=\"si\">%(min_length)d</span><span class=\"s2\"> characters.&quot;</span><span class=\"p\">),</span>\n                <span class=\"n\">code</span><span class=\"o\">=</span><span class=\"s1\">&#39;password_too_short&#39;</span><span class=\"p\">,</span>\n                <span class=\"n\">params</span><span class=\"o\">=</span><span class=\"p\">{</span><span class=\"s1\">&#39;min_length&#39;</span><span class=\"p\">:</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">min_length</span><span class=\"p\">},</span>\n            <span class=\"p\">)</span>\n\n    <span class=\"k\">def</span><span class=\"w\"> </span><span class=\"nf\">get_help_text</span><span class=\"p\">(</span><span class=\"bp\">self</span><span class=\"p\">):</span>\n        <span class=\"k\">return</span> <span class=\"n\">_</span><span class=\"p\">(</span>\n            <span class=\"s2\">&quot;Your password must contain at least </span><span class=\"si\">%(min_length)d</span><span class=\"s2\"> characters.&quot;</span>\n            <span class=\"o\">%</span> <span class=\"p\">{</span><span class=\"s1\">&#39;min_length&#39;</span><span class=\"p\">:</span> <span class=\"bp\">self</span><span class=\"o\">.</span><span class=\"n\">min_length</span><span class=\"p\">}</span>\n        <span class=\"p\">)</span>\n</code></pre></div>\n<p>You can also implement <code class=\"docutils literal notranslate\"><span class=\"pre\">password_changed(password,</span> <span class=\"pre\">user=None</span></code>), which will\nbe called after a successful password change. That can be used to prevent\npassword reuse, for example. However, if you decide to store a user’s previous\npasswords, you should never do so in clear text.</p>\n</section>\n</section>","rootId":"password-management-in-django","toc":[{"title":"How Django stores passwords","anchor":"how-django-stores-passwords","children":[{"title":"Using Argon2 with Django","anchor":"using-argon2-with-django","children":[]},{"title":"Using bcrypt with Django","anchor":"using-bcrypt-with-django","children":[]},{"title":"Increasing the work factor","anchor":"increasing-the-work-factor","children":[{"title":"PBKDF2 and bcrypt","anchor":"pbkdf2-and-bcrypt","children":[]},{"title":"Argon2","anchor":"argon2","children":[]}]},{"title":"Password upgrading","anchor":"password-upgrading","children":[]},{"title":"Password upgrading without requiring a login","anchor":"password-upgrading-without-requiring-a-login","children":[]},{"title":"Included hashers","anchor":"included-hashers","children":[]},{"title":"Writing your own hasher","anchor":"writing-your-own-hasher","children":[]}]},{"title":"Manually managing a user’s password","anchor":"module-django.contrib.auth.hashers","children":[]},{"title":"Password validation","anchor":"module-django.contrib.auth.password_validation","children":[{"title":"Enabling password validation","anchor":"enabling-password-validation","children":[]},{"title":"Included validators","anchor":"included-validators","children":[]},{"title":"Integrating validation","anchor":"integrating-validation","children":[]},{"title":"Writing your own validator","anchor":"writing-your-own-validator","children":[]}]}],"breadcrumbs":[{"docname":"topics/index","title":"Using Django","url":"/es/1.10/topics/"},{"docname":"topics/auth/index","title":"User authentication in Django","url":"/es/1.10/topics/auth/"}],"prev":{"docname":"topics/auth/default","title":"Using the Django authentication system","url":"/es/1.10/topics/auth/default/"},"next":{"docname":"topics/auth/customizing","title":"Customizing authentication in Django","url":"/es/1.10/topics/auth/customizing/"},"formats":{"html":"/es/1.10/topics/auth/passwords/","markdown":"/es/1.10/topics/auth/passwords.md","json":"/es/1.10/topics/auth/passwords.json"},"source":"https://github.com/django/django/blob/stable/1.10.x/docs/topics/auth/passwords.txt","official":"https://docs.djangoproject.com/es/1.10/topics/auth/passwords/","inVersions":["6.1","6.0","5.2","5.1","5.0","4.2","4.1","4.0","3.2","3.1","3.0","2.2","2.1","2.0","1.11","1.10","1.9"],"inLocales":["en","fr","ja","id","pt-br","es","el","pl"]}