$max) return false; $bytes = intdiv($prefix, 8); $bits = $prefix % 8; if ($bytes > 0 && substr($ipBin, 0, $bytes) !== substr($networkBin, 0, $bytes)) return false; if ($bits === 0) return true; $mask = (0xFF << (8 - $bits)) & 0xFF; return (ord($ipBin[$bytes]) & $mask) === (ord($networkBin[$bytes]) & $mask); } function mnbt_request_from_trusted_proxy() { $remote = isset($_SERVER['REMOTE_ADDR']) ? trim((string)$_SERVER['REMOTE_ADDR']) : ''; $trusted = getenv('MNBT_TRUSTED_PROXIES'); if ($remote === '' || $trusted === false || trim($trusted) === '') return false; foreach (explode(',', $trusted) as $proxy) { $proxy = trim($proxy); if ($proxy !== '' && mnbt_ip_in_cidr($remote, $proxy)) return true; } return false; } function mnbt_request_is_secure() { if (!empty($_SERVER['HTTPS']) && strtolower((string)$_SERVER['HTTPS']) !== 'off') return true; if (isset($_SERVER['SERVER_PORT']) && (int)$_SERVER['SERVER_PORT'] === 443) return true; if (!mnbt_request_from_trusted_proxy()) return false; $proto = isset($_SERVER['HTTP_X_FORWARDED_PROTO']) ? strtolower(trim(explode(',', (string)$_SERVER['HTTP_X_FORWARDED_PROTO'])[0])) : ''; return $proto === 'https'; } function mnbt_set_cookie($name, $value, $expires = 0, $httpOnly = true, $sameSite = 'Lax', $path = '/') { $options = ['expires' => (int)$expires, 'path' => $path, 'secure' => mnbt_request_is_secure(), 'httponly' => (bool)$httpOnly, 'samesite' => $sameSite]; if (PHP_VERSION_ID >= 70300) return setcookie($name, $value, $options); return setcookie($name, $value, (int)$expires, $path . '; samesite=' . $sameSite, '', $options['secure'], $options['httponly']); } function mnbt_set_auth_cookie($name, $value, $expires) { $deleteAt = time() - 604800; foreach (['/admin', '/user', '/app_plugins/user_info', '/app_plugins/user_info/lib'] as $legacyPath) mnbt_set_cookie($name, '', $deleteAt, true, 'Lax', $legacyPath); return mnbt_set_cookie($name, $value, $expires, true, 'Lax', '/'); } function mnbt_rotate_login_session() { if (session_status() === PHP_SESSION_ACTIVE) session_regenerate_id(true); mnbt_csrf_token(true); } function mnbt_csrf_token($rotate = false) { if ($rotate || empty($_SESSION['mnbt_csrf_token']) || !is_string($_SESSION['mnbt_csrf_token'])) { try { $_SESSION['mnbt_csrf_token'] = bin2hex(random_bytes(32)); } catch (Throwable $e) { $_SESSION['mnbt_csrf_token'] = hash('sha256', uniqid((string)mt_rand(), true) . session_id()); } } $token = $_SESSION['mnbt_csrf_token']; if (!headers_sent() && (!isset($_COOKIE['MNBT_CSRF_TOKEN']) || !hash_equals((string)$_COOKIE['MNBT_CSRF_TOKEN'], $token))) { mnbt_set_cookie('MNBT_CSRF_TOKEN', $token, 0, false, 'Lax'); $_COOKIE['MNBT_CSRF_TOKEN'] = $token; } return $token; } function mnbt_csrf_request_token() { foreach (['HTTP_X_CSRF_TOKEN', 'HTTP_X_XSRF_TOKEN'] as $header) if (!empty($_SERVER[$header])) return (string)$_SERVER[$header]; foreach (['_csrf', 'csrf_token', '_token'] as $key) if (isset($_POST[$key]) && is_string($_POST[$key])) return $_POST[$key]; // 双提交 Cookie 兜底:页面未注入 meta 时前端可从 MNBT_CSRF_TOKEN 读取并回传 if (isset($_COOKIE['MNBT_CSRF_TOKEN']) && is_string($_COOKIE['MNBT_CSRF_TOKEN']) && $_COOKIE['MNBT_CSRF_TOKEN'] !== '') return $_COOKIE['MNBT_CSRF_TOKEN']; return ''; } function mnbt_csrf_verify($token = null) { $expected = isset($_SESSION['mnbt_csrf_token']) && is_string($_SESSION['mnbt_csrf_token']) ? $_SESSION['mnbt_csrf_token'] : ''; if ($expected === '') return false; $actual = $token === null ? mnbt_csrf_request_token() : (string)$token; if ($actual !== '' && hash_equals($expected, $actual)) return true; // header/post 携带旧 token 不匹配时,回退校验双提交 cookie,覆盖旧页面/轮换后的场景 if (isset($_COOKIE['MNBT_CSRF_TOKEN']) && is_string($_COOKIE['MNBT_CSRF_TOKEN']) && $_COOKIE['MNBT_CSRF_TOKEN'] !== '') { return hash_equals($expected, $_COOKIE['MNBT_CSRF_TOKEN']); } return false; } function mnbt_csrf_is_safe_method($method = null) { $method = strtoupper($method === null ? ($_SERVER['REQUEST_METHOD'] ?? 'GET') : (string)$method); return in_array($method, ['GET', 'HEAD', 'OPTIONS'], true); } function mnbt_csrf_enabled() { if (defined('MNBT_CSRF_ENABLED')) return (bool)MNBT_CSRF_ENABLED; $env = getenv('MNBT_CSRF_ENABLED'); if ($env !== false && trim($env) !== '') return !in_array(strtolower(trim($env)), ['0', 'false', 'off', 'no'], true); return true; } function mnbt_csrf_fail() { $expected = isset($_SESSION['mnbt_csrf_token']) && is_string($_SESSION['mnbt_csrf_token']) ? $_SESSION['mnbt_csrf_token'] : ''; $provided = mnbt_csrf_request_token(); $source = ''; foreach (['HTTP_X_CSRF_TOKEN', 'HTTP_X_XSRF_TOKEN'] as $h) if (!empty($_SERVER[$h])) { $source = 'header'; break; } if ($source === '') foreach (['_csrf', 'csrf_token', '_token'] as $k) if (isset($_POST[$k]) && is_string($_POST[$k])) { $source = 'post'; break; } if ($source === '' && isset($_COOKIE['MNBT_CSRF_TOKEN']) && $_COOKIE['MNBT_CSRF_TOKEN'] !== '') $source = 'cookie'; error_log(sprintf('[MNBT CSRF] fail path=%s method=%s has_session_token=%d provided_len=%d source=%s has_cookie=%d ip=%s', $_SERVER['REQUEST_URI'] ?? '', $_SERVER['REQUEST_METHOD'] ?? '', $expected !== '' ? 1 : 0, strlen($provided), $source, isset($_COOKIE['MNBT_CSRF_TOKEN']) ? 1 : 0, $_SERVER['REMOTE_ADDR'] ?? '')); http_response_code(419); @header('Content-Type: application/json; charset=UTF-8'); if (function_exists('json_exit_error')) json_exit_error('CSRF 验证失败,请刷新页面后重试'); if (function_exists('json_exit')) json_exit('CSRF 验证失败,请刷新页面后重试'); exit('{"success":false,"code":"CSRF 验证失败,请刷新页面后重试","msg":"CSRF 验证失败,请刷新页面后重试","redirect":null}'); } function mnbt_csrf_validate_request($exempt = false) { if ($exempt || !mnbt_csrf_enabled() || mnbt_csrf_is_safe_method()) return true; if (!mnbt_csrf_verify()) mnbt_csrf_fail(); return true; } function mnbt_csrf_field() { return ''; } function mnbt_csrf_head() { $token = htmlspecialchars(mnbt_csrf_token(), ENT_QUOTES, 'UTF-8'); return ''; } function mnbt_csrf_inject_html($html) { if (!is_string($html) || $html === '') return $html; if (!preg_match('/\A\s*(?:]*>\s*)?]*>/i', $html)) return $html; if (!preg_match('/]*>[\s\S]*<\/head\s*>/i', $html)) return $html; if (preg_match('/]*\bname\s*=\s*(["\'])csrf-token\1/i', $html)) return $html; return preg_replace('/<\/head\s*>/i', mnbt_csrf_head() . '', $html, 1); } function curl_get($url) { $ch=curl_init($url); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Linux; U; Android 4.4.1; zh-cn; R815T Build/JOP40D) AppleWebKit/533.1 (KHTML, like Gecko)Version/4.0 MQQBrowser/4.5 Mobile Safari/533.1'); curl_setopt($ch, CURLOPT_TIMEOUT, 10); $content=curl_exec($ch); curl_close($ch); return($content); } function daddslashes($string, $force = 0, $strip = FALSE) { !defined('MAGIC_QUOTES_GPC') && define('MAGIC_QUOTES_GPC', false); if(!MAGIC_QUOTES_GPC || $force) { if(is_array($string)) { foreach($string as $key => $val) { $string[$key] = daddslashes($val, $force, $strip); } } else { $string = addslashes($strip ? stripslashes($string) : $string); } } return $string; } function authcode($string, $operation = 'DECODE', $key = '', $expiry = 0) { $ckey_length = 4; $key = md5($key ? $key : ENCRYPT_KEY); $keya = md5(substr($key, 0, 16)); $keyb = md5(substr($key, 16, 16)); $keyc = $ckey_length ? ($operation == 'DECODE' ? substr($string, 0, $ckey_length): substr(md5(microtime()), -$ckey_length)) : ''; $cryptkey = $keya.md5($keya.$keyc); $key_length = strlen($cryptkey); $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 = ($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++) { $a = ($a + 1) % 256; $j = ($j + $box[$a]) % 256; $tmp = $box[$a]; $box[$a] = $box[$j]; $box[$j] = $tmp; $result .= chr(ord($string[$i]) ^ ($box[($box[$a] + $box[$j]) % 256])); } if($operation == 'DECODE') { 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 { return $keyc.str_replace('=', '', base64_encode($result)); } } function showmsg($content = '未知的异常',$type = 4,$back = false) { switch($type) { case 1: $panel="success"; break; case 2: $panel="info"; break; case 3: $panel="warning"; break; case 4: $panel="danger"; break; } echo '

提示信息

'; echo $content; if ($back) { echo '
<< 返回上一页'; } else echo '
<< 返回上一页'; echo '
'; } function sysmsg($msg = '未知的异常',$die = true) { ?> 站点提示信息 站点提示信息'; echo $msg; ?> query_prepare("INSERT INTO `MN_log` (`czuser`, `date`, `lx`, `lr`, `ip`, `qk`) VALUES (?, ?, ?, ?, ?, ?)", [$czuser_hsk_v, $data_time_rq_sjv, $lx_hsk_v, $lr_hsk_v, $ip_hxer_ipsz_envc, $qk_hsk_v])) return '1'; $error = method_exists($DBZHER, 'error') ? $DBZHER->error() : '未知数据库错误'; error_log('[MNBT业务日志失败] '.$error.' | '.$lx_hsk_v.' '.$lr_hsk_v); return '0'.$error; } catch (Throwable $e) { error_log('[MNBT业务日志异常] '.$e->getMessage().' | '.$lx_hsk_v.' '.$lr_hsk_v); return '0'.$e->getMessage(); } } function mnbt_log($user = '系统', $type = '系统日志', $content = '内容', $status = '记录', $db = null) { global $DB; if ($db === null && isset($DB)) $db = $DB; return logjl($user, $type, $content, $status, $db); } function mnbt_api_compat_mode() { global $conf; // api_compat='1' 时对外主机 API 还原 1.81 老协议行为,供未升级对接模块的客户过渡使用 return isset($conf['api_compat']) && (string)$conf['api_compat'] === '1'; } function send_post($url, $post_data) { if(!is_array($post_data)) $post_data=[]; $postdata = http_build_query($post_data); $options = array( 'http' => array( 'method' => 'POST', 'header' => 'Content-type:application/x-www-form-urlencoded', 'content' => $postdata, 'timeout' => 4// 超时时间(单位:s) ) ); $context = stream_context_create($options); $result = file_get_contents($url, false, $context); return $result; } function ary_asd($mn_conf){ $fh_ry='"'; foreach($mn_conf as $sne=>$via){ $fh_ry_r='"'; if(is_array($via)){ $via='array('.ary_asd($via).')'; $fh_ry_r=''; } if(is_numeric($via)){ $fh_ry_r=''; } if($kr_sxy==""){ $kr_sxy.=$fh_ry.$sne.$fh_ry.'=>'.$fh_ry_r.$via.$fh_ry_r; } else{ $kr_sxy.=','.$fh_ry.$sne.$fh_ry.'=>'.$fh_ry_r.$via.$fh_ry_r; } } return $kr_sxy; } function deldir($dir) { //删除目录下的文件 //先删除目录下的文件: $dh = opendir($dir); while ($file = readdir($dh)) { if ($file != "." && $file != "..") { $fullpath = $dir. "/".$file; if (!is_dir($fullpath)) { unlink($fullpath); } else { deldir($fullpath); } } } closedir($dh); //删除当前文件夹: if (rmdir($dir)) { return true; } else { return false; } } function dirfiles($data, $type) { //控制面板返回的文件数据处理$filename如果不为false则为获取指定文件名数据 $userinisf=0; $printarry = []; foreach($data as $val) { $arr = []; //为本次循环新建一个数组存放本条次数据 $valarr = explode(";", $val); if($valarr[0]=='.user.ini' && $type=='file'){$userinisf=1; continue;} //防跨站配置文件不用显示给用户 $arr['name'] = $valarr[0]; //文件名 $arr['type'] = $type; //文件类型 $arr['mtime'] = $valarr[2]; //修改时间戳 $arr['size'] = $valarr[1]; //文件大小 $arr['download']=$valarr[6]; //是否外链分享 $printarry[] = $arr; //数组存储 unset($arr); } return ["file"=>$printarry,"couts"=>$userinisf]; } function delval_array($arr,$value,$path=''){ //删除一维数组中指定的值,同时取消名称不规范的目录/文件,并且判断执行完成后是否存在文件可删 foreach ($arr as $k=>$v){ if($path.$v==$value)unset($arr[$k]); //删除指定的值的参数 if(strpos($v,'/'))unset($arr[$k]); //删除值不规范的参数 $arr = array_merge($arr); } if(empty($arr)){ //判断是否为空数组 return false; }else{ return $arr; } } function zipfile($path,$zipth,$paths='/',$filetext=false) { //压缩目录(被压缩的目录,此函数上一次执行的位置,压缩包存放处,程序配置文件内容) $zip = new \ZipArchive; $pathname=substr($path,strripos($path,'/')+1); //获取目录名 if($zip->open($zipth, \ZIPARCHIVE::CREATE)===true) { //新建一个压缩包 if($paths=='/') { $zip->addEmptyDir($pathname); //新建一个目录 } else { $zip->addEmptyDir($pathname.$paths); //新建一个目录 } if($filetext) { $zip->addFromString($pathname.$paths.'mnbt_file_conf.json', $filetext); } $list=scandir($path.$paths); foreach ($list as $val) { if($val=='.' || $val=='..')continue; if(is_dir($path.$paths.$val)) { //如果是目录则执行函数 $zip->close(); //关闭压缩包 if($paths=='/') { zipfile($path,$zipth,$paths.$val.'/',false); } else { zipfile($path,$zipth,$paths.'/'.$val.'/',false); } } else { //文件 $zip->addFile($path.$paths.$val,$pathname.$paths.$val); } } } else return ["code"=>4,"msg"=>"错误!创建压缩包失败!"]; $zip->close(); //关闭压缩包 return ["code"=>1,"msg"=>"程序打包完成"]; } function mnbt_json_encode($code, $extra = [], $success = null) { $msg = is_scalar($code) ? (string)$code : '返回信息'; if ($success === null) { $success = !isset($extra['qk']) || (string)$extra['qk'] !== '4'; } if (class_exists('Response')) { $result = Response::build($code, $msg, empty($extra) ? null : $extra, null, $success); } else { $result = [ 'success' => (bool)$success, 'code' => $code, 'msg' => $msg, 'redirect' => null, ]; if (!empty($extra)) $result['data'] = $extra; } foreach ($extra as $k => $v) { $result[$k] = $v; } $json = json_encode($result, JSON_UNESCAPED_UNICODE); if ($json === false) $json = '{"success":false,"code":"' . addslashes((string)$code) . '","msg":"JSON编码失败"}'; return $json; } function json_exit($code, $extra = []) { exit(mnbt_json_encode($code, $extra)); } function json_exit_success($msg, $extra = []) { $extra['qk'] = 1; exit(mnbt_json_encode($msg, $extra, true)); } function json_exit_error($msg, $extra = []) { $extra['qk'] = 4; exit(mnbt_json_encode($msg, $extra, false)); } function json_echo($code, $extra = []) { echo mnbt_json_encode($code, $extra); } function json_return($code, $extra = []) { return mnbt_json_encode($code, $extra); } ?>