mirror of
https://github.com/tznb1/TwoNav.git
synced 2025-08-10 08:51:49 +00:00
v2.0.34-20230809
This commit is contained in:
161
system/Authenticator.php
Normal file
161
system/Authenticator.php
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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)){
|
||||
|
||||
@@ -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']);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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'){
|
||||
|
||||
@@ -1 +1 @@
|
||||
v2.0.33-20230802
|
||||
v2.0.34-20230809
|
||||
Reference in New Issue
Block a user