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); 
    }
?>
maksim
Maksim(一笑,吡罗),PHPer,Goper
OωO
开启隐私评论,您的评论仅作者和评论双方可见