在面对这个网络世界的时候,密码安全老是各个公司和用户都很是关心的一个内容,毕竟如今你们不论是休闲娱乐仍是学习购物都是经过网上的账号来进行消费的,因此咱们一般会给用户的密码进行加密。在加密的时候,常常会听到“加盐”这个词,这是什么意思呢?php
咱们一般会将用户的密码进行 Hash 加密,若是不加盐,即便是两层的 md5 都有可能经过彩虹表的方式进行破译。彩虹表就是在网上搜集的各类字符组合的 Hash 加密结果。而加盐,就是人为的经过一组随机字符与用户原密码的组合造成一个新的字符,从而增长破译的难度。就像作饭同样,加点盐味道会更好。mysql
接下来,咱们经过代码来演示一种比较安全的加盐方式。git
首先,咱们建一个简单的用户表。这个表里只有四个字段,在这里仅做为测试使用。程序员
CREATE TABLE `zyblog_test_user` ( `id` int(11) NOT NULL AUTO_INCREMENT, `username` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '用户名', `password` varchar(255) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '密码', `salt` char(4) COLLATE utf8mb4_bin DEFAULT NULL COMMENT '盐', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin;
而后定义两个方式,一个用来生成盐,一个用来生成加盐后的 Hash 密码。github
/** * 随机生成四位字符串的salt * 也能够根据实际状况使用6位或更长的salt */ function generateSalt() { // 使用随机方式生成一个四位字符 $chars = array_merge(range('A', 'Z'), range('a', 'z'), range('0', '9')); for ($i = 0; $i < 4; $i++) { $str .= $chars[mt_rand(0, count($chars) - 1)]; } return $str; } /** * 密码生成 * 使用两层hash,将salt加在第二层 * sha1后再加salt而后再md5 */ function generateHashPassword($password, $salt) { return md5(sha1($password) . $salt); }
generateSalt() 方法很简单,就是生成一个随机的四位字符的字符串,咱们使用大小写加数字的形式生成这个字符串。这就是传说中的“盐”。sql
接下来咱们就可使用 generateHashPassword() 方法为用户的原密码加盐。在这里咱们第一层先使用 sha1() 对原密码进行一次 Hash ,而后使用这个 Hash 值拼接盐字符串后再进行 md5() 加密。最后加密出来的 Hash 值就很难在彩虹表中找到了。即便找到,也只是上层 sha1() 拼接盐字符串的内容,用户的原文密码毕竟还有一层加密。数据库
剩下的就是咱们进行出入库的注册登陆测试了。安全
$pdo = new PDO('mysql:host=localhost;dbname=blog_test;charset=utf8mb4', 'root', ''); $username = 'ZyBlog1'; $password = '123456'; // 注册 function register($username, $password) { global $pdo; // 首先判断用户是否已注册 $pre = $pdo->prepare("SELECT COUNT(id) FROM zyblog_test_user WHERE username = :username"); $pre->bindParam(':username', $username); $pre->execute(); $result = $pre->fetchColumn(); // 若是用户名存在,则没法注册 if ($result > 0) { echo '用户名已注册!', PHP_EOL; return 0; } // 生成salt $salt = generateSalt(); // 密码进行加盐hash处理 $password = generateHashPassword($password, $salt); // 插入新用户 $pre = $pdo->prepare("insert into zyblog_test_user(username, password, salt) values(?, ?, ?)"); $pre->bindValue(1, $username); $pre->bindValue(2, $password); $pre->bindValue(3, $salt); $pre->execute(); return $pdo->lastInsertId(); } $userId = register($username, $password); if ($userId > 0) { echo '注册成功!用户ID为:' . $userId, PHP_EOL; } // 注册成功!用户ID为:1 // 查询数据库中的数据 $sth = $pdo->prepare("SELECT * FROM zyblog_test_user"); $sth->execute(); $result = $sth->fetchAll(PDO::FETCH_ASSOC); print_r($result); // Array // ( // [0] => Array // ( // [id] => 1 // [username] => ZyBlog1 // [password] => bbff8283d0f90625015256b742b0e694 // [salt] => xOkb // ) // ) // 登陆时验证 function login($username, $password) { global $pdo; // 先根据用户名查表 $pre = $pdo->prepare("SELECT * FROM zyblog_test_user WHERE username = :username"); $pre->bindParam(':username', $username); $pre->execute(); $result = $pre->fetch(PDO::FETCH_ASSOC); // 用户名存在并得到用户信息后 if ($result) { // 根据用户表中的salt字段生成hash密码 $password = generateHashPassword($password, $result['salt']); // 比对hash密码确认登陆是否成功 if ($password == $result['password']) { return true; } } return false; } $isLogin = login($username, $password); if ($isLogin) { echo '登陆成功!', PHP_EOL; } else { echo '登陆失败,用户名或密码错误!', PHP_EOL; } // 登陆成功!
代码仍是比较简单的,在注册的时候,咱们直接对用户密码进行加密后入库。主要关注的地方是在登陆时,咱们先根据用户名查找出对应的用户信息。而后将用户登陆提交上来的原文密码进行加密,与数据库中的原文密码进行对比验证,密码验证成功便可判断用户登陆成功。网络
另外还须要注意的是,咱们的盐字符串也是要存到数据库中的。毕竟在登陆的时候咱们仍是须要将用户的原文密码与这个盐字符串进行组合加密以后才能进行密码的匹配。学习
这样加密后的代码其实想经过彩虹表来破解基本上是很难了。在几年前 CSDN 的账号泄露事件中,你们发现做为中文程序员世界最大的网站居然是明文存储的密码,这就为攻击者提供了一大堆用户的明文经常使用密码。由于你们都喜欢用同一个用户名和密码注册不一样的网站,因此无论其余怎么加盐都是没用的,毕竟原文密码是对的,拿到这样一个网站的数据库中的用户明文密码后,就能够经过这些密码去尝试这些用户在其余网站是否是用了相同的账号名和密码注册了账号。因此在平常生活中,咱们重要的一些网站账号、密码尽可能仍是使用不一样的内容,若是记不住的话,可使用一些带加密能力的记事本软件进行保存,这样会更加安全。而咱们程序员,则应该始终都将用户的密码及重要信息进行加密处理,这是一种基本的职业规范。
测试代码:
各自媒体平台都可搜索【硬核项目经理】