在面对这个网络世界的时候,明码平安总是各个公司和用户都十分关怀的一个内容,毕竟当初大家不论是休闲娱乐还是学习购物都是通过网上的帐号来进行生产的,所以咱们通常会给用户的明码进行加密。在加密的时候,常常会听到“加盐”这个词,这是什么意思呢?
咱们通常会将用户的明码进行 Hash 加密,如果不加盐,即便是两层的 md5 都有可能通过彩虹表的形式进行破译。彩虹表就是在网上收集的各种字符组合的 Hash 加密后果。而加盐,就是人为的通过一组随机字符与用户原明码的组合造成一个新的字符,从而减少破译的难度。就像做饭一样,加点盐滋味会更好。
接下来,咱们通过代码来演示一种比拟平安的加盐形式。
首先,咱们建一个简略的用户表。这个表里只有四个字段,在这里仅作为测试应用。
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 明码。
/** * 随机生成四位字符串的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() 办法很简略,就是生成一个随机的四位字符的字符串,咱们应用大小写加数字的模式生成这个字符串。这就是传说中的“盐”。
接下来咱们就能够应用 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 的帐号泄露事件中,大家发现作为中文程序员世界最大的网站居然是明文存储的明码,这就为攻击者提供了一大堆用户的明文罕用明码。因为大家都喜爱用同一个用户名和明码注册不同的网站,所以不论其余怎么加盐都是没用的,毕竟原文明码是对的,拿到这样一个网站的数据库中的用户明文明码后,就能够通过这些明码去尝试这些用户在其余网站是不是用了雷同的帐号名和明码注册了帐号。所以在日常生活中,咱们重要的一些网站帐号、明码尽量还是应用不同的内容,如果记不住的话,能够应用一些带加密能力的记事本软件进行保留,这样会更加平安。而咱们程序员,则应该始终都将用户的明码及重要信息进行加密解决,这是一种根本的职业标准。
测试代码:
https://github.com/zhangyue0503/dev-blog/blob/master/php/202003/source/%E4%BB%80%E4%B9%88%E5%8F%AB%E7%BB%99%E5%AF%86%E7%A0%81%E2%80%9C%E5%8A%A0%E7%9B%90%E2%80%9D%EF%BC%9F%E5%A6%82%E4%BD%95%E5%AE%89%E5%85%A8%E7%9A%84%E4%B8%BA%E4%BD%A0%E7%9A%84%E7%94%A8%E6%88%B7%E5%AF%86%E7%A0%81%E2%80%9C%E5%8A%A0%E7%9B%90%E2%80%9D%EF%BC%9F.php
各自媒体平台均可搜寻【硬核项目经理】