06 更加安全的密码管理
安全620 字
■ 密码设置
场景:用户注册、密码重置、密码找回时。
说明:密码设置时,应该满足8位及以上长度,含大小写字母、数字及特殊字符等的要求。用户密码设置必须经过后端校验,不允许设置不满足复杂度要求的密码。
PHP示例:后端验证密码复杂度。
<?php
function validPwd($pwd) {
if (strlen($pwd) < 8) {
echo "密码必须包含至少含有8个字符,请返回修改!";
return FALSE;
}
if (preg_match_all('/[A-Z]/', $pwd, $o) < 1) {
echo "密码必须包含至少一个大写字母,请返回修改!";
return FALSE;
}
if (preg_match_all('/[a-z]/', $pwd, $o) < 1) {
echo "密码必须包含至少一个小写字母,请返回修改!";
return FALSE;
}
if (preg_match_all('/[0-9]/', $pwd, $o) < 1) {
echo "密码必须包含至少一个数字,请返回修改!";
return FALSE;
}
if (preg_match_all('/[~!@#$%^&*()\-_=+{};:<,.>?]/', $pwd, $o) < 1) {
echo "密码必须包含至少一个特殊符号:[~!@#$%^&*()\-_=+{};:<,.>?],请返回修改!";
return FALSE;
}
return TRUE;
}
?>
■ 密码存储
场景:用户提交的密码在服务器端保存时,需要做转换避免明文存储后泄露。
说明:用户密码存储时,应采用哈希算法(如SHA1)计算用户密码和唯一随机盐值(Salt)的摘要值,保存其摘要和Salt值,建议分开存储这两个值。
PHP示例:使用摘要算法BCrypt存储用户密码。
<?php
try {
$email = filter_input(INPUT_POST, 'email', FILTER_VALIDATE_EMAIL);
if (!$email) {
throw new Exception('非法的Email');
}
$password = filter_input(INPUT_POST, 'password');
if (!$password || mb_strlen($password) < 8) {
throw new Exception('密码长度必须大于8位');
}
$sql = "SELECT username FROM user WHERE username=:username";
$stmt = $db->prepare($sql);
$stmt->execute(array(
':username' => $email
));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if ($row) {
exit('用户Email已存在');
}
$passwordHash = password_hash(
$password,
PASSWORD_DEFAULT,
['cost' => 12]
);
if ($passwordHash === false) {
throw new Exception('Password hash failed');
}
$sql_insert = "INSERT INTO `user` (username,password) VALUES (:username,:password)";
$stmt = $db->prepare($sql_insert);
$stmt->execute(array(
':username' => $email,
':password' => $passwordHash,
));
$insert_id = $db->lastinsertid();
if ($insert_id) {
header('HTTP/1.1 302 Redirect');
header('Location: login.html');
}
} catch (Exception $e) {
header('HTTP/1.1 400 Bad request');
echo $e->getMessage();
}
?>
■ 密码修改
场景:密码修改。
说明:用户修改密码时,修改操作需通过手机号、账户或者邮箱进行二次身份验证。密码变更时,应短信或者邮件通知用户是否是本人操作,告知其安全风险。
PHP示例:
<?php
session_start();
try {
$email = filter_input(INPUT_POST, 'email');
$password = filter_input(INPUT_POST, 'password');
$sql = "SELECT id,password FROM user WHERE username=:username";
$stmt = $db->prepare($sql);
$stmt->execute(array(
':username' => $email
));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
exit('用户不存在');
}
if (password_verify($password, $row['password']) === false) {
exit('密码错误!');
}
$_SESSION['user_reset'] = 'ok';
$_SESSION['user_email'] = $email;
header('HTTP/1.1 302 Redirect');
header('Location: passwdReset.php');
} catch (Exception $e) {
header('HTTP/1.1 401 Unauthorized');
echo $e->getMessage();
}
?>
■ 密码找回
场景:密码找回
说明:用户密码找回时,后端需要对注册手机号或邮箱进行二次验证,验证码和验证链接应发送至预先注册的地址,并设置有效期以防止暴力破解。密保问题,应当支持尽可能随机的问题提问。在多个验证操作中,要对各验证机制进行排序,以防出现跳过前面验证机制直接到最后一步认证的安全风险。
示例【Java代码】:
<?php
session_start();
try {
$email = filter_input(INPUT_POST, 'email');
$sms_code = filter_input(INPUT_POST, 'sms_code');
$sql = "SELECT id FROM user WHERE username=:username";
$stmt = $db->prepare($sql);
$stmt->execute(array(
':username' => $email
));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$row) {
exit('用户不存在');
}
if (($sms_code === $_SESSION['sms_code']) == false) {
exit('短信验证码错误!');
}
$_SESSION['user_pwdback'] = 'ok';
header('HTTP/1.1 302 Redirect');
header('Location: passwdReset.php');
} catch (Exception $e) {
header('HTTP/1.1 401 Unauthorized');
echo $e->getMessage();
}
?>
■ 密码使用
场景:密码使用。
说明:应用开发中禁止设置万能密码、硬编码明文的密码、使用数据库管理员账户操作、不同用户公用账户操作或者将密码输出到日志文件或者控制台。
PHP项目配置示例:config.ini。
datahost = 172.10.2.2
username = web
password = xxxxxx
PHP示例:PHP包含使用。
<?php
$arrs = parse_ini_file("config.ini", true);
$conn = mysqli_connect($arrs['datahost'], $arrs['username'], $arrs['password']) or die(mysqli_connect_error());
mysqli_select_db($conn, 'web');
mysqli_query($conn, 'set names utf8');
$sqls = 'SELECT * FROM users';
$rest = mysqli_query($conn, $sqls);
while ($row = mysqli_fetch_array($rest)) {
print_r($row);
}
?>