<p>密码是salted,是的。在散列之前将salt添加到密码中,以确保散列在<a href="https://security.stackexchange.com/questions/379/what-are-rainbow-tables-and-how-are-they-used">rainbow table attack</a>中不可用。</p>
<p>因为salt是在每次调用函数时随机生成的,所以生成的密码散列也不同。返回的散列包含生成的salt,因此仍然可以正确验证密码。</p>
<p>演示:</p>
<pre><code>>>> from werkzeug.security import generate_password_hash
>>> generate_password_hash('foobar')
'pbkdf2:sha1:1000$tYqN0VeL$2ee2568465fa30c1e6680196f8bb9eb0d2ca072d'
>>> generate_password_hash('foobar')
'pbkdf2:sha1:1000$XHj5nlLU$bb9a81bc54e7d6e11d9ab212cd143e768ea6225d'
</code></pre>
<p>这两个字符串不同;但是包含足够的信息来验证密码,因为生成的salt包含在每个字符串中:</p>
<pre><code># pbkdf2:sha1:1000$tYqN0VeL$2ee2568465fa30c1e6680196f8bb9eb0d2ca072d
^^^^^^^^^^^^^^^^ salt ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
algo info ^^^^^^^^ actual hash of the password
(PBKDF2 applied SHA1 1000 times)
</code></pre>
<p>因为随机盐是<code>tYqN0VeL</code>对于one和<code>XHj5nlLU</code>,所以得到的散列也不同。</p>
<p>仍然可以根据以下任一哈希验证<code>foobar</code>密码:</p>
<pre><code>>>> from werkzeug.security import check_password_hash
>>> check_password_hash('pbkdf2:sha1:1000$tYqN0VeL$2ee2568465fa30c1e6680196f8bb9eb0d2ca072d', 'foobar')
True
>>> check_password_hash('pbkdf2:sha1:1000$XHj5nlLU$bb9a81bc54e7d6e11d9ab212cd143e768ea6225d', 'foobar')
True
</code></pre>
<p>另见</p>
<ul>
<li><a href="https://crypto.stackexchange.com/questions/1776/can-you-help-me-understand-what-a-cryptographic-salt-is">Can you help me understand what a cryptographic “salt” is?</a>(密码学.SE)</li>
<li><a href="https://security.stackexchange.com/questions/14025/why-is-using-salt-more-secure">Why is using salt more secure?</a>(Security.SE)</li>
</ul>