land_api输出缓冲加固防HTML污染;admin前端apiCall容错HTML响应
This commit is contained in:
@@ -2950,19 +2950,36 @@ async function apiCall(action, params = {}, method = 'GET') {
|
||||
const url = new URL(LAND_API, window.location.href);
|
||||
url.searchParams.set('action', action);
|
||||
url.searchParams.set('secret', SECRET);
|
||||
let r;
|
||||
if (method === 'GET') {
|
||||
for (const [k, v] of Object.entries(params)) url.searchParams.set(k, v);
|
||||
const r = await fetch(url);
|
||||
return r.json();
|
||||
r = await fetch(url);
|
||||
} else {
|
||||
const body = new URLSearchParams(params);
|
||||
body.set('secret', SECRET);
|
||||
const r = await fetch(url, {
|
||||
r = await fetch(url, {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
|
||||
body: body.toString()
|
||||
});
|
||||
return r.json();
|
||||
}
|
||||
// ★ 2026-10-11 防 HTML 污染:后端异常时可能返回 HTML 错误页,
|
||||
// 统一转成带状态码与响应片段的友好错误,不再直接 r.json() 抛 "Unexpected token '<'"
|
||||
const text = await r.text().catch(() => '');
|
||||
const t = text.replace(/^\uFEFF/, '').trim();
|
||||
if (!t) {
|
||||
throw new Error('接口 ' + action + ' 返回空响应 (HTTP ' + r.status + ')');
|
||||
}
|
||||
if (t.charAt(0) === '<') {
|
||||
console.error('[apiCall] ' + action + ' 返回 HTML 而非 JSON, HTTP ' + r.status + ':', t.substring(0, 300));
|
||||
throw new Error('接口 ' + action + ' 返回了 HTML 页面 (HTTP ' + r.status
|
||||
+ '),服务器可能出错,请查看后端日志。片段: ' + t.substring(0, 100));
|
||||
}
|
||||
try {
|
||||
return JSON.parse(t);
|
||||
} catch (e) {
|
||||
console.error('[apiCall] ' + action + ' JSON 解析失败, HTTP ' + r.status + ':', t.substring(0, 300));
|
||||
throw new Error('接口 ' + action + ' 返回无效 JSON (HTTP ' + r.status + '): ' + t.substring(0, 100));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,39 @@
|
||||
* POST: Java端推送领地数据
|
||||
* GET: admin/玩家面板读取领地数据
|
||||
*/
|
||||
// ★ 2026-10-11 防 HTML 污染加固(对齐 api/admin.php 的既有做法):
|
||||
// display_errors 关闭(fatal/警告不再吐 HTML 错误页)+ 输出缓冲 +
|
||||
// shutdown 兜底:只要最终输出不是合法 JSON 开头,就替换为 JSON 错误,
|
||||
// 保证管理面板永远收到 JSON(页面不再报 "Unexpected token '<'")。
|
||||
ini_set('display_errors', '0');
|
||||
ini_set('log_errors', '1');
|
||||
ob_start();
|
||||
register_shutdown_function(function () {
|
||||
$err = error_get_last();
|
||||
$fatal = $err && in_array($err['type'], [E_ERROR, E_PARSE, E_CORE_ERROR, E_COMPILE_ERROR], true);
|
||||
$buf = '';
|
||||
while (ob_get_level() > 0) {
|
||||
$buf .= ob_get_clean();
|
||||
}
|
||||
$trim = ltrim($buf);
|
||||
if ($buf !== '' && $trim !== '' && $trim[0] !== '{' && $trim[0] !== '[') {
|
||||
// 输出被 HTML/文本污染 → 整体替换为 JSON 错误(保留片段便于排查)
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => 'API 输出被非 JSON 内容污染' . ($fatal && !empty($err['message']) ? ': ' . $err['message'] : ''),
|
||||
'snippet' => mb_substr($trim, 0, 160),
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
return;
|
||||
}
|
||||
if ($fatal && $buf === '') {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => '服务器内部错误: ' . (isset($err['message']) ? $err['message'] : 'unknown'),
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
return;
|
||||
}
|
||||
echo $buf;
|
||||
});
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
error_reporting(E_ERROR | E_PARSE);
|
||||
// 取 SECRET_KEY(插件与 Web 端约定的通信密钥,值在 config.php 里由部署者自己填)。
|
||||
|
||||
Reference in New Issue
Block a user