v2.0.34-20230809

This commit is contained in:
MI15\Win
2023-08-09 14:52:37 +08:00
parent cec87b24f2
commit 1ece1135ea
13 changed files with 405 additions and 29 deletions

161
system/Authenticator.php Normal file
View File

@@ -0,0 +1,161 @@
<?php
class PHPGangsta_GoogleAuthenticator
{
protected $_codeLength = 6;
public function createSecret($secretLength = 16)
{
$validChars = $this->_getBase32LookupTable();
if ($secretLength < 16 || $secretLength > 128) {
throw new Exception('Bad secret length');
}
$secret = '';
$rnd = false;
if (function_exists('random_bytes')) {
$rnd = random_bytes($secretLength);
} elseif (function_exists('mcrypt_create_iv')) {
$rnd = mcrypt_create_iv($secretLength, MCRYPT_DEV_URANDOM);
} elseif (function_exists('openssl_random_pseudo_bytes')) {
$rnd = openssl_random_pseudo_bytes($secretLength, $cryptoStrong);
if (!$cryptoStrong) {
$rnd = false;
}
}
if ($rnd !== false) {
for ($i = 0; $i < $secretLength; ++$i) {
$secret .= $validChars[ord($rnd[$i]) & 31];
}
} else {
throw new Exception('No source of secure random');
}
return $secret;
}
public function getCode($secret, $timeSlice = null)
{
if ($timeSlice === null) {
$timeSlice = floor(time() / 30);
}
$secretkey = $this->_base32Decode($secret);
$time = chr(0).chr(0).chr(0).chr(0).pack('N*', $timeSlice);
$hm = hash_hmac('SHA1', $time, $secretkey, true);
$offset = ord(substr($hm, -1)) & 0x0F;
$hashpart = substr($hm, $offset, 4);
$value = unpack('N', $hashpart);
$value = $value[1];
$value = $value & 0x7FFFFFFF;
$modulo = pow(10, $this->_codeLength);
return str_pad($value % $modulo, $this->_codeLength, '0', STR_PAD_LEFT);
}
public function getQRCodeGoogleUrl($name, $secret, $title = null, $params = array())
{
$width = !empty($params['width']) && (int) $params['width'] > 0 ? (int) $params['width'] : 200;
$height = !empty($params['height']) && (int) $params['height'] > 0 ? (int) $params['height'] : 200;
$level = !empty($params['level']) && array_search($params['level'], array('L', 'M', 'Q', 'H')) !== false ? $params['level'] : 'M';
$urlencoded = urlencode('otpauth://totp/'.$name.'?secret='.$secret.'');
if (isset($title)) {
$urlencoded .= urlencode('&issuer='.urlencode($title));
}
return "https://api.qrserver.com/v1/create-qr-code/?data=$urlencoded&size=${width}x${height}&ecc=$level";
}
public function verifyCode($secret, $code, $discrepancy = 1, $currentTimeSlice = null)
{
if ($currentTimeSlice === null) {
$currentTimeSlice = floor(time() / 30);
}
if (strlen($code) != 6) {
return false;
}
for ($i = -$discrepancy; $i <= $discrepancy; ++$i) {
$calculatedCode = $this->getCode($secret, $currentTimeSlice + $i);
if ($this->timingSafeEquals($calculatedCode, $code)) {
return true;
}
}
return false;
}
public function setCodeLength($length)
{
$this->_codeLength = $length;
return $this;
}
protected function _base32Decode($secret)
{
if (empty($secret)) {
return '';
}
$base32chars = $this->_getBase32LookupTable();
$base32charsFlipped = array_flip($base32chars);
$paddingCharCount = substr_count($secret, $base32chars[32]);
$allowedValues = array(6, 4, 3, 1, 0);
if (!in_array($paddingCharCount, $allowedValues)) {
return false;
}
for ($i = 0; $i < 4; ++$i) {
if ($paddingCharCount == $allowedValues[$i] &&
substr($secret, -($allowedValues[$i])) != str_repeat($base32chars[32], $allowedValues[$i])) {
return false;
}
}
$secret = str_replace('=', '', $secret);
$secret = str_split($secret);
$binaryString = '';
for ($i = 0; $i < count($secret); $i = $i + 8) {
$x = '';
if (!in_array($secret[$i], $base32chars)) {
return false;
}
for ($j = 0; $j < 8; ++$j) {
$x .= str_pad(base_convert(@$base32charsFlipped[@$secret[$i + $j]], 10, 2), 5, '0', STR_PAD_LEFT);
}
$eightBits = str_split($x, 8);
for ($z = 0; $z < count($eightBits); ++$z) {
$binaryString .= (($y = chr(base_convert($eightBits[$z], 2, 10))) || ord($y) == 48) ? $y : '';
}
}
return $binaryString;
}
protected function _getBase32LookupTable()
{
return 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', '2', '3', '4', '5', '6', '7',
'=',
);
}
private function timingSafeEquals($safeString, $userString)
{
if (function_exists('hash_equals')) {
return hash_equals($safeString, $userString);
}
$safeLen = strlen($safeString);
$userLen = strlen($userString);
if ($userLen != $safeLen) {
return false;
}
$result = 0;
for ($i = 0; $i < $userLen; ++$i) {
$result |= (ord($safeString[$i]) ^ ord($userString[$i]));
}
return $result === 0;
}
}

View File

@@ -392,7 +392,7 @@ if($_GET['type'] == 'upload'){
}
//数据库清除
if(!empty($_POST['TABLE'])){
$TABLE = ["user_categorys","user_links","user_pwd_group","user_share","user_apply"];
$TABLE = ["user_categorys","user_links","user_pwd_group","user_share","user_apply","user_article_list"];
foreach($_POST['TABLE'] as $key =>$value){
if(in_array($key,$TABLE)){
delete_db($key,['uid'=>UID]);
@@ -418,7 +418,7 @@ if($_GET['type'] == 'upload'){
//文件删除
if(!empty($_POST['FILE'])){
$FILE = ["MessageBoard","favicon"];
$FILE = ["MessageBoard","favicon","upload"];
foreach($_POST['FILE'] as $key =>$value){
$path = DIR.'/data/user/'.U.'/'.$key;
if(in_array($key,$FILE) && is_dir($path)){

View File

@@ -47,7 +47,7 @@ if(!defined('DIR')){
$info['file_db'] = $info['backup_dir'] .'/'. $info['file'].'.db3';
$info['file_info'] = $info['backup_dir'] .'/'. $info['file'].'.info';
$info['file_gz'] = $info['backup_dir'] .'/'. $info['file'].'.tar';
$info['table_arr'] = ['user_config','user_categorys','user_links','user_pwd_group','user_apply','user_share'];
$info['table_arr'] = ['user_config','user_categorys','user_links','user_pwd_group','user_apply','user_share','user_article_list'];
$info['lock'] = DIR.'/data/user/'.U.'/lock.'.UID;
if (!extension_loaded('phar')) {
msg(-1,'不支持phar扩展');
@@ -167,7 +167,7 @@ if(!defined('DIR')){
}
//遍历删除用户数据
$info['table_arr'] = ['user_config','user_categorys','user_links','user_pwd_group','user_apply','user_share'];
$info['table_arr'] = ['user_config','user_categorys','user_links','user_pwd_group','user_apply','user_share','user_article_list'];
foreach($info['table_arr'] as $table_name){
//删除数据
@@ -186,8 +186,15 @@ if(!defined('DIR')){
$where['name'] = $table_name;
$where['LIMIT'] = [($page - 1) * $limit,$limit];
$datas = $MyDB->select('backup','data',$where);
foreach($datas as $data){
foreach($datas as $key => $data){
$data = unserialize($data);
//处理null
foreach ($data as $key => $value) {
if ($value === null) {
$data[$key] = '';
}
}
if(isset($data['id'])){
unset($data['id']);
}

View File

@@ -951,7 +951,7 @@ function write_security_setting(){
'login_page'=>['v'=>['admin','index','auto'],'msg'=>'登录成功参数错误'],
'Password2'=>['empty'=>true]
];
$LoginConfig = unserialize($USER_DB['LoginConfig']);
foreach ($datas as $key => $data){
if($data['int']){
$LoginConfig[$key] = ($_POST[$key] >= $data['min'] && $_POST[$key] <= $data['max'])?intval($_POST[$key]):msg(-1,$data['msg']);
@@ -1211,6 +1211,65 @@ function write_user_password(){
msg(1,'修改成功');
}
//读双重验证
function read_totp(){
global $USER_DB;
if($USER_DB['Password'] !== Get_MD5_Password($_POST['Password'],$USER_DB['RegTime'])){
msg(-1102,'密码错误,请核对后再试!');
}
$LoginConfig = unserialize($USER_DB['LoginConfig']);
if(empty($LoginConfig['totp_key'])){
require DIR . '/system/Authenticator.php';
$totp = new PHPGangsta_GoogleAuthenticator();
$key = $totp->createSecret();
msgA(['code'=>2,'msg'=>'未开启双重验证','key'=> $key ]);
}
msgA(['code'=>1,'msg'=>'已开启双重验证']);
}
//写双重验证
function write_totp(){
global $USER_DB;
if($USER_DB['Password'] !== Get_MD5_Password($_POST['Password'],$USER_DB['RegTime'])){
msg(-1102,'密码错误,请核对后再试!');
}
if($_GET['type'] === 'delete'){ //删除双重验证
$LoginConfig = unserialize($USER_DB['LoginConfig']);
if(empty($LoginConfig['totp_key'])){
msgA(['code'=>-1,'msg'=>'未开启双重验证',]);
}
$LoginConfig['totp_key'] = '';
update_db("global_user", ["LoginConfig"=>$LoginConfig],["ID"=>UID],[1,'操作成功']);
}elseif($_GET['type'] === 'set'){ //设置双重验证
//必填项验证
if(empty($_POST['key'])){
msgA(['code'=>-1,'msg'=>'Key不能为空']);
}elseif(empty($_POST['code'])){
msgA(['code'=>-1,'msg'=>'验证码不能为空']);
}
$LoginConfig = unserialize($USER_DB['LoginConfig']);
if(!empty($LoginConfig['totp_key'])){
msgA(['code'=>-1,'msg'=>'已开启双重验证,无法继续开启!']);
}
//载入totp库
require DIR . '/system/Authenticator.php';
$totp = new PHPGangsta_GoogleAuthenticator();
$checkResult = $totp->verifyCode($_POST['key'], $_POST['code'], 2);
if(!$checkResult){
msgA(['code'=>-1,'msg'=>'验证失败,请重试']);
}
//写入数据库
$LoginConfig = unserialize($USER_DB['LoginConfig']);
$LoginConfig['totp_key'] = $_POST['key'];
update_db("global_user", ["LoginConfig"=>$LoginConfig],["ID"=>UID],[1,'操作成功']);
}else{
msg(-1,'请求参数有误');
}
}
//查Token
function read_token(){
global $USER_DB;

View File

@@ -40,11 +40,25 @@ if(strlen($Password)!==32){
msg(-1,"浏览器UA长度异常,请更换浏览器!");
}
$LoginConfig = unserialize( $USER_DB['LoginConfig'] );
//开启双重验证时验证OTP验证码
if(!empty($LoginConfig['totp_key'])){
if(empty($_POST['otp_code'])){
msgA(['code'=>-1,'msg'=>'您已开启双重验证,请输入OTP验证码']);
}
require DIR . '/system/Authenticator.php';
$totp = new PHPGangsta_GoogleAuthenticator();
$checkResult = $totp->verifyCode($LoginConfig['totp_key'], $_POST['otp_code'], 2);
if(!$checkResult){
msgA(['code'=>-1,'msg'=>'OTP验证码错误,请重试!']);
}
}
//计算请求密码和数据库的对比
if(Get_MD5_Password($Password,$USER_DB["RegTime"]) === $USER_DB["Password"]){
update_db("user_log", ["description" => "请求登录>登录成功"], ["id"=>$log_id]);
Set_key($USER_DB);
$LoginConfig = unserialize( $USER_DB['LoginConfig'] );
if(empty($LoginConfig['login_page']) || $LoginConfig['login_page'] == 'admin'){
$url = "./?c=admin&u={$USER_DB['User']}";
}elseif($LoginConfig['login_page'] == 'index'){

View File

@@ -1 +1 @@
v2.0.33-20230802
v2.0.34-20230809