各位代码界的“靓仔靓女”们,晚上好!欢迎来到今晚的密码安全“脱口秀”!今天咱们要扒的是WordPress里负责“守护贞操”(保护密码安全)的wp_hash_password()
函数,看看它到底是怎么利用phpass
这个“秘密武器”来对我们的密码进行加密的。
开场白:密码的世界,水很深!
在互联网的世界里,密码的重要性不言而喻。想象一下,你的银行账户、邮箱、社交媒体账号,甚至你的WordPress网站,都依赖于一串你自认为足够复杂的字符。但是,直接存储用户的原始密码简直就是裸奔,一旦数据库泄露,所有用户的信息都会暴露无遗。
所以,我们需要一种方式,把密码“变丑”,让别人即使拿到这串“丑密码”,也无法轻易还原成原始密码。 这就是密码哈希的目的!
第一幕:wp_hash_password()
函数登场!
wp_hash_password()
函数是WordPress中用于对用户密码进行哈希处理的关键函数。简而言之,它接收一个明文密码作为输入,然后使用加密算法将其转换为一个难以破解的哈希值,并将其存储在数据库中。
我们来看看wp_hash_password()
函数的真面目(简化版):
function wp_hash_password( $password ) {
global $wp_hasher;
if ( empty( $wp_hasher ) ) {
require_once ABSPATH . 'wp-includes/class-phpass.php';
$wp_hasher = new PasswordHash( 8, true );
}
return $wp_hasher->HashPassword( trim( $password ) );
}
哇哦!代码很简单嘛!但是别被表面现象迷惑了,精彩的都在背后呢。
global $wp_hasher;
: 这行代码声明了一个全局变量$wp_hasher
。 这个变量将用于存储一个PasswordHash
类的实例,这个类才是真正干活的。if ( empty( $wp_hasher ) ) { ... }
: 这部分代码检查$wp_hasher
是否为空。如果是空的,就表示我们还没有创建PasswordHash
类的实例。require_once ABSPATH . 'wp-includes/class-phpass.php';
: 如果$wp_hasher
为空,这行代码会包含class-phpass.php
文件。这个文件是phpass
库的核心,它包含了用于密码哈希的PasswordHash
类。ABSPATH
是WordPress 常量,代表WordPress 的根目录。$wp_hasher = new PasswordHash( 8, true );
: 这行代码创建了PasswordHash
类的一个新实例,并将其赋值给$wp_hasher
。PasswordHash
类的构造函数接受两个参数:8
: 这表示哈希的迭代次数的log2值。迭代次数越高,哈希过程越慢,破解的难度也越大,但同时也会增加服务器的负担。true
: 这表示启用Portable哈希。Portable哈希意味着生成的哈希值可以在不同的PHP版本和服务器配置之间兼容。
return $wp_hasher->HashPassword( trim( $password ) );
: 这行代码调用PasswordHash
类的HashPassword()
方法,将明文密码作为参数传递给它。HashPassword()
方法会对密码进行哈希处理,并返回哈希后的密码。trim()
函数用于去除密码字符串开头和结尾的空白字符。
第二幕:phpass
库闪亮登场!
phpass
是一个专门用于生成和验证密码哈希的PHP库。它由Solar Designer(Openwall 项目的创始人)开发,旨在提供一种简单而安全的密码哈希解决方案。WordPress 使用 phpass
库,因为它具有以下优点:
- 安全性高:
phpass
使用bcrypt算法(或者MD5,SHA256等,但是推荐bcrypt),这是一种被广泛认可的强密码哈希算法。 - 可移植性好: 生成的哈希值可以在不同的PHP版本和服务器配置之间兼容。
- 易于使用:
phpass
提供了简单的API,可以方便地生成和验证密码哈希。
让我们深入了解phpass
库中的PasswordHash
类,它是密码哈希的核心。
class PasswordHash {
var $itoa64;
var $iteration_count_log2;
var $portable_hashes;
var $random_state;
function PasswordHash($iteration_count_log2, $portable_hashes) {
$this->itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
if ($iteration_count_log2 < 4 || $iteration_count_log2 > 31)
$iteration_count_log2 = 8;
$this->iteration_count_log2 = $iteration_count_log2;
$this->portable_hashes = $portable_hashes;
$this->random_state = microtime();
if (function_exists('getmypid'))
$this->random_state .= getmypid();
}
function HashPassword($password) {
$random = $this->get_random_bytes(16);
$hash = crypt($password, $this->gensalt($random));
if (strlen($hash) == 34)
return $hash;
# Returning '*' on error is safe here, but please use crypt_private()
# in your own code.
return '*';
}
function gensalt($input) {
$output = '$P$';
$output .= $this->itoa64[min($this->iteration_count_log2 +
((PHP_VERSION >= '5' && defined('CRYPT_BLOWFISH')
&& CRYPT_BLOWFISH) ? 0 : 3), 30)];
$output .= $this->encode64($input, 16);
return $output;
}
function get_random_bytes($count) {
$output = '';
if (is_readable('/dev/urandom') &&
($fh = @fopen('/dev/urandom', 'rb'))) {
$output = fread($fh, $count);
fclose($fh);
}
if (strlen($output) < $count) {
$output = '';
for ($i = 0; $i < $count; $i += 16) {
$this->random_state = md5(microtime() . $this->random_state);
$output .= md5($this->random_state, true);
}
$output = substr($output, 0, $count);
}
return $output;
}
function encode64($input, $count) {
$output = '';
$i = 0;
do {
$value = ord($input[$i++]);
$output .= $this->itoa64[$value & 0x3f];
if ($i < $count)
$value |= ord($input[$i]) << 8;
$output .= $this->itoa64[($value >> 6) & 0x3f];
if ($i++ >= $count)
break;
if ($i < $count)
$value |= ord($input[$i]) << 16;
$output .= $this->itoa64[($value >> 12) & 0x3f];
if ($i++ >= $count)
break;
$output .= $this->itoa64[($value >> 18) & 0x3f];
} while ($i < $count);
return $output;
}
function CheckPassword($password, $stored_hash) {
$hash = crypt($password, $stored_hash);
if ($hash[0] == '*')
$hash = '*';
return $hash == $stored_hash;
}
}
让我们逐个分析PasswordHash
类中的关键方法:
__construct($iteration_count_log2, $portable_hashes)
: 构造函数,用于初始化类的属性。$iteration_count_log2
: 哈希的迭代次数的log2值。迭代次数越高,哈希过程越慢,破解的难度也越大。$portable_hashes
: 是否启用Portable哈希。$itoa64
: 一个字符串,用于将二进制数据编码为可打印的ASCII字符。
HashPassword($password)
: 对密码进行哈希处理。- 使用
get_random_bytes()
函数生成16个随机字节作为salt。 - 使用
gensalt()
函数生成salt字符串。 - 使用
crypt()
函数对密码进行哈希处理。 - 如果哈希成功,返回哈希后的密码,否则返回
'*'
。
- 使用
gensalt($input)
: 生成salt字符串。- 根据
iteration_count_log2
和PHP版本生成salt字符串的前缀。 - 使用
encode64()
函数将随机字节编码为可打印的ASCII字符。
- 根据
get_random_bytes($count)
: 生成指定数量的随机字节。- 尝试从
/dev/urandom
读取随机字节。 - 如果
/dev/urandom
不可用,使用md5()
函数生成伪随机字节。
- 尝试从
encode64($input, $count)
: 将二进制数据编码为可打印的ASCII字符。CheckPassword($password, $stored_hash)
: 检查密码是否与存储的哈希值匹配。- 使用
crypt()
函数对密码进行哈希处理。 - 将哈希后的密码与存储的哈希值进行比较。
- 如果匹配,返回
true
,否则返回false
。
- 使用
第三幕: 密码哈希的原理
密码哈希的本质是将任意长度的输入(密码)转换为固定长度的输出(哈希值)。这个过程是不可逆的,也就是说,无法从哈希值反推出原始密码。
一个好的密码哈希算法应该具备以下特点:
- 单向性: 无法从哈希值反推出原始密码。
- 抗碰撞性: 很难找到两个不同的输入,它们产生相同的哈希值。
- 确定性: 相同的输入始终产生相同的哈希值。
- 快速哈希: 哈希过程应该足够快,以便在用户登录时快速验证密码。
- 慢速破解: 即使攻击者获得了哈希值,破解密码也应该非常困难。
bcrypt 算法是目前最流行的密码哈希算法之一。它通过以下方式来增强安全性:
- Salt: Salt是一个随机字符串,它与密码一起进行哈希处理。Salt的作用是防止彩虹表攻击。
- Iteration: Iteration是指哈希算法重复执行的次数。Iteration次数越高,破解密码的难度就越大。
第四幕:破解密码的那些事儿
虽然密码哈希可以有效地保护密码安全,但它并不是万无一失的。攻击者可以使用各种技术来破解密码,例如:
- 彩虹表攻击: 彩虹表是一个预先计算好的哈希值表,攻击者可以使用彩虹表来查找与给定哈希值对应的密码。
- 暴力破解: 暴力破解是指尝试所有可能的密码组合,直到找到与给定哈希值对应的密码。
- 字典攻击: 字典攻击是指使用一个包含常见密码的字典,尝试查找与给定哈希值对应的密码。
为了防止密码被破解,我们应该:
- 使用强密码: 强密码应该包含大小写字母、数字和符号,并且长度应该足够长。
- 定期更换密码: 定期更换密码可以降低密码被破解的风险。
- 不要在多个网站上使用相同的密码: 如果一个网站的密码被破解,攻击者可以使用相同的密码来访问其他网站。
- 启用双因素认证: 双因素认证可以增加账户的安全性。
- 使用安全的密码哈希算法: bcrypt 算法是目前最安全的密码哈希算法之一。
第五幕:WordPress 如何验证密码?
验证密码的过程正好和哈希密码的过程相反。WordPress使用wp_check_password()
函数来验证用户输入的密码是否与存储在数据库中的哈希值匹配。
function wp_check_password( $password, $hash, $user_id = '' ) {
global $wp_hasher;
if ( empty( $wp_hasher ) ) {
require_once ABSPATH . 'wp-includes/class-phpass.php';
$wp_hasher = new PasswordHash( 8, true );
}
$check = $wp_hasher->CheckPassword( trim( $password ), $hash );
/**
* Filters whether the given password matches the hashed password.
*
* @since 2.5.0
*
* @param bool $check Whether the password matches the hash.
* @param string $password The user-supplied password to check.
* @param string $hash A hash retrieved from the database to compare to.
* @param string $user_id The user ID.
*/
return apply_filters( 'check_password', $check, $password, $hash, $user_id );
}
这个函数也用到了 phpass
库,它先是确保 $wp_hasher
对象存在,如果不存在就创建一个。然后,它调用 $wp_hasher->CheckPassword()
来比较用户输入的密码哈希值和数据库中存储的哈希值。
$wp_hasher->CheckPassword()
内部会使用相同的哈希算法和 salt 对用户输入的密码进行哈希,然后将结果与存储的哈希值进行比较。如果两者匹配,说明密码正确,否则密码错误。
总结:密码安全,任重道远!
今天我们一起深入了解了WordPress 中wp_hash_password()
函数和phpass
库,了解了密码哈希的原理,以及如何使用bcrypt 算法来保护密码安全。希望今天的“脱口秀”能帮助大家更好地理解密码安全的重要性,并采取相应的措施来保护自己的密码安全。
记住,密码安全不是一蹴而就的事情,而是一个持续的过程。我们需要不断学习新的安全知识,并及时更新我们的安全措施。
感谢大家的观看,我们下期再见! 祝大家代码无bug,生活更精彩!