$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 ''; } function sysmsg($msg = '未知的异常',$die = true) { ?>