php加密解密的使用
一、项目说明
在开发大屏时,需要在前端输入密码,以便认证用户的登录权限。但是本次的项目需求是,没有数据库存储密码,且希望每次密码都不相同,最好是动态密码。
二、项目分析
- 没有数据库储存密码,意味着密码将以明文或密文的形式储存于php文件中。从安全性和保密性来说,不是很强。登录、验证、判断即可。
- 动态密码,既能满足动态不断变换又能是通用规则,内部员工知晓即可的条件,就自有不断变换的时间了。
基于此,做了个简单的DEMO。输入年时间作为动态密码,在后端验证时,任意输入密码字符串,只要包含当前符合规则的联系数字即可。现在是2023年4月24日 18:05,生成的密码分为三种情况:
- 20231805,8位数字;
- 231805,6位数字;
- 1805,4位数字;
1.js外部文件
<title>漏刻有时密码测试工具</title>
<script type="text/javascript" src="js/jquery.min.js"></script>
<script type="text/javascript" src="js/functions.js"></script>
<!--layui封装库-->
<script src="js/layui/layui.js" charset="utf-8"></script>
<link rel="stylesheet" href="js/layui/css/layui.css">
2.HTML容器构建
<div class="layui-fluid" style="margin-top: 20px;">
<form class="layui-form layui-form-pane" lay-filter="component-form-group">
<div class="layui-card">
<div class="layui-card-header" style="font-weight: bold;text-align: center;">漏刻有时密码测试工具</div>
<div class="layui-card-body" style="padding: 15px;">
<div class="layui-form-item">
<label class="layui-form-label">测试密码<span class="x-red">*</span></label>
<div class="layui-input-inline"><input type="password" id="password" name="password" autocomplete="off" lay-verify="password" class="layui-input"></div>
<div class="layui-form-mid layui-word-aux"><span id="passTips" class="x-red"></span></div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">密码明文<span class="x-red">*</span></label>
<div class="layui-input-inline"><input type="text" id="passshow" name="passshow" autocomplete="off" lay-verify="required" class="layui-input" readonly></div>
</div>
<div class="layui-form-item">
<a class="layui-btn layui-btn-fluid layui-btn-normal" lay-filter="save" id="L_add" lay-submit=""><i class="layui-icon layui-icon-search"></i> 确定配置 </a>
</div>
</div>
</div>
</form>
</div>
3.layui前端验证
//同步密码;
$("#password").change(function(){
var $pass= $("#password").val();
$("#passshow").val($pass);
})
layui.use(['form'], function () {
var $ = layui.jquery;
var form = layui.form;
//验证规则
form.verify({
password: [/(?=.*[0-9])(?=.*[a-zA-Z]).{6,30}/, '密码必须同时包含字母和数字且至少6位']
});
//监听提交;
form.on('submit(save)', function () {
$.ajax({
type: "post",
url: "indexDeal.php?act=annual",
async: true,
data: {
pass: $('#password').val()
},
dataType: "json",
success: function (res) {
if (res.code == 1) {
layer.msg(res.msg + "成功", {icon: 1, time: 1000}, function () {
$("#passTips").html("您的密码为:"+res.pass);
});
}else{
layer.msg(res.msg + "失败", {icon: 2, time: 1000}, function () {
$("#passTips").html("您的密码为:"+res.pass);
});
}
}
});
return false;
});
});
4.php后端验证
$act = $_GET['act'];
if ($act == 'annual') {
//获取前端输入密码
$pass = trim($_POST['pass']);
$res['code'] = dynamicCode($pass, 2);
$res['pass'] = $pass;
$res['msg'] = "密码已验证:";
die(json_encode($res));
}
封装函数
/*判断是否包含*/
function getCode($str, $server_pass)
{
if (strpos($str, $server_pass) === FALSE) {
return 0;
} else {
return 1;
}
}
密码验证规则
/*动态密码*/
function dynamicCode($str, $type = '1')
{
/*$server_pass
* 默认获取系统的时间,即服务器的时间
* 默认客户端时间和系统时间同步;
* 返回0,代表验证失败;1,代表验证成功;
*/
/*验证密码
* 1,前端获取的字符串是否包含连续的$server_pass,仿密码锁原理,只要联系输对即可
* */
switch ($type) {
case 1://8位数字密码
$server_pass = date('YHi');//时间格式20232330,代表2023年23:30
return getCode($str, $server_pass);
break;
case 2:
//六位密码
$server_pass = date('yHi');//时间格式20232330,代表2023年23:30
return getCode($str, $server_pass);
break;
case 3:
//四位密码
$server_pass = date('Hi');//时间格式20232330,代表2023年23:30
return getCode($str, $server_pass);
break;
default:
return 0;
}
}
strpos内置函数
定义和用法
strpos(string,find,start)
strpos() f函数查找字符串在另一字符串中第一次出现的位置(区分大小写)。
相关函数:
- strrpos() - 查找字符串在另一字符串中最后一次出现的位置(区分大小写)
- stripos() -查找字符串在另一字符串中第一次出现的位置(不区分大小写)
- strripos() - 查找字符串在另一字符串中最后一次出现的位置(不区分大小写)
三、经典的核心加密函数
1.Discuz!开发之核心加密解密函数
Discuz!开发的使用异或运算进行加密和解密的函数,Discuz!所有产品都是用这个函数。Discuz!整合UCenter的同步登录中authcode()就扮演者重要的角色。在同步登录(从项目登录到UCenter)的过程中,authcode()把用户的登录信息进行加密,因为没有加密的数据在传递过程中容易被截取,会暴露了用户的信息,authcode()的作用就是给传递的数据提供加密保护作用。在数据到达终端(UCenter)时authcode()再把加密的数据进行反向解密,还原数据。
AuthCode动态密匙加解密并设置密文有效期,相同的明文会生成不同密文。
- AuthCode的算法非常经典和使用,在实际使用的过程中必须设置$key,防止被截获予以破解;
- 在使用get方式传递参数时,产生的+容易被当成空格处理,因此在接受参数的时候,需要予以替换解决。
<?php
/* 参数解释
$string: 明文 或 密文
$operation:DECODE表示解密,其它表示加密
$key: 密匙
$expiry:密文有效期*/
if(!function_exists('AuthCode'))
{
function AuthCode($string, $operation='DECODE', $key='', $expiry=0)
{
// 动态密匙长度,相同的明文会生成不同密文就是依靠动态密匙
// 加入随机密钥,可以令密文无任何规律,即便是原文和密钥完全相同,加密结果也会每次不同,增大破解难度。
// 取值越大,密文变动规律越大,密文变化 = 16 的 $ckey_length 次方
// 当此值为 0 时,则不产生随机密钥
$cfg_auth_key = '';
$ckey_length = 4;
// 密匙
$key = md5($key ? $key : $cfg_auth_key);
// 密匙a会参与加解密
$keya = md5(substr($key, 0, 16));
// 密匙b会用来做数据完整性验证
$keyb = md5(substr($key, 16, 16));
// 密匙c用于变化生成的密文
$keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : '';
// 参与运算的密匙
$cryptkey = $keya.md5($keya.$keyc);
$key_length = strlen($cryptkey);
// 明文,前10位用来保存时间戳,解密时验证数据有效性,10到26位用来保存$keyb(密匙b),解密时会通过这个密匙验证数据完整性
// 如果是解码的话,会从第$ckey_length位开始,因为密文前$ckey_length位保存 动态密匙,以保证解密正确
$string = $operation == 'DECODE' ? base64_decode(substr($string, $ckey_length)) : sprintf('%010d', $expiry ? $expiry + time() : 0).substr(md5($string.$keyb), 0, 16).$string;
$string_length = strlen($string);
$result = '';
$box = range(0, 255);
$rndkey = array();
// 产生密匙簿
for($i = 0; $i <= 255; $i++)
{
$rndkey[$i] = ord($cryptkey[$i % $key_length]);
}
// 用固定的算法,打乱密匙簿,增加随机性,好像很复杂,实际上并不会增加密文的强度
for($j = $i = 0; $i < 256; $i++)
{
//$j是三个数相加与256取余
$j = ($j + $box[$i] + $rndkey[$i]) % 256;
$tmp = $box[$i];
$box[$i] = $box[$j];
$box[$j] = $tmp;
}
// 核心加解密部分
for($a = $j = $i = 0; $i < $string_length; $i++)
{
//在上面基础上再加1 然后和256取余
$a = ($a + 1) % 256;
$j = ($j + $box[$a]) % 256;//$j加$box[$a]的值 再和256取余
$tmp = $box[$a];
$box[$a] = $box[$j];
$box[$j] = $tmp;
// 从密匙簿得出密匙进行异或,再转成字符,加密和解决时($box[($box[$a] + $box[$j]) % 256])的值是不变的。
$result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256]));
}
if($operation == 'DECODE')
{
// substr($result, 0, 10) == 0 验证数据有效性
// substr($result, 0, 10) - time() > 0 验证数据有效性
// substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16) 验证数据完整性
// 验证数据有效性,请看未加密明文的格式
if((substr($result, 0, 10) == 0 || substr($result, 0, 10) - time() > 0) && substr($result, 10, 16) == substr(md5(substr($result, 26).$keyb), 0, 16))
{
return substr($result, 26);
}
else
{
return '';
}
}
else
{
// 把动态密匙保存在密文里,这也是为什么同样的明文,生产不同密文后能解密的原因
// 因为加密后的密文可能是一些特殊字符,复制过程可能会丢失,所以用base64编码
return $keyc.str_replace('=', '', base64_encode($result));
}
}
}
2.常用简单加密解密函数
下面的加密解密函数是一段比较简单的算法,仅限于数字、字母和大小写,在对保密性要求不高的情形下,可以使用。
function lockAuth($tex, $key, $type = "encode")
{
$chrArr = array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9');
if ($type == "decode") {
if (strlen($tex) < 14) return false;
$verity_str = substr($tex, 0, 8);
$tex = substr($tex, 8);
if ($verity_str != substr(md5($tex), 0, 8)) {
//完整性验证失败
return false;
}
}
$key_b = $type == "decode" ? substr($tex, 0, 6) : $chrArr[rand() % 62] . $chrArr[rand() % 62] . $chrArr[rand() % 62] . $chrArr[rand() % 62] . $chrArr[rand() % 62] . $chrArr[rand() % 62];
$rand_key = $key_b . $key;
$rand_key = md5($rand_key);
$tex = $type == "decode" ? base64_decode(substr($tex, 6)) : $tex;
$texlen = strlen($tex);
$reslutstr = "";
for ($i = 0; $i < $texlen; $i++) {
$reslutstr .= $tex{$i} ^ $rand_key{$i % 32};
}
//加密
if ($type != "decode") {
$reslutstr = trim($key_b . base64_encode($reslutstr), "==");
$reslutstr = substr(md5($reslutstr), 0, 8) . $reslutstr;
}
return $reslutstr;
}
@漏刻有时