From fc8c82b1887bc51bc200412290b1ffe9617bdf12 Mon Sep 17 00:00:00 2001 From: ypsdf1 Date: Fri, 9 Oct 2026 18:56:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=85=A8=E6=96=B0=E9=83=A8?= =?UTF-8?q?=E7=BD=B2=E9=A6=96=E8=B7=91=E9=A1=BA=E5=BA=8F=EF=BC=9A=E8=A3=85?= =?UTF-8?q?=E5=BA=93=E5=85=88=E4=BA=8E2FA=E5=BC=95=E5=AF=BC=EF=BC=9Binstal?= =?UTF-8?q?l=E8=A1=A8=E5=8D=95=E8=A1=A5=E4=BB=A4=E7=89=8C=E9=80=8F?= =?UTF-8?q?=E4=BC=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - security.php 新增 secInstallPending():install.php 在场且无 install.lock = 装库未完成 - 引导模式下所有入口先 302 到 install.php,装完自删后才轮到 admin_2fa_setup.php - admin_login/admin_2fa_setup 兜住本机直连与已引导未装库两种入口(secTokenUrl 带令牌) - install.php 表单隐藏 token 字段,已引导站点 POST 也能过第三层门禁 --- admin_2fa_setup.php | 9 +++++++++ admin_login.php | 8 ++++++++ install.php | 5 +++++ security.php | 19 +++++++++++++++++++ 4 files changed, 41 insertions(+) diff --git a/admin_2fa_setup.php b/admin_2fa_setup.php index 8d4d062..08a5edd 100644 --- a/admin_2fa_setup.php +++ b/admin_2fa_setup.php @@ -17,6 +17,15 @@ secEnsureSession(); secGatePage(true); $boot = secBootSessionActive(); +// ★ 首跑顺序(2026-10-09):装库先于安全配置 —— install.php 还没跑完(文件在场、 +// 无 install.lock)时,本页先让位给数据库安装页;装完它自删,再回来配 2FA/白名单/令牌。 +// 引导模式下 secGatePage 已先行 302,这里兜住「已引导 / 本机直连」两种入口; +// 已引导站点带 ?token= 过 install.php 的第三层门禁(令牌为空时 secTokenUrl 原样返回)。 +if (secInstallPending()) { + header('Location: ' . secTokenUrl('install.php')); + exit; +} + // ★ 首次部署:管理密码还是分发占位符 → 走初始化模式。 // 旧行为在这里直接跳登录页,是个死胡同:登录要密码,而这时候根本没有可用密码。 $needInit = !secAdminPassConfigured(); diff --git a/admin_login.php b/admin_login.php index d1627a0..cb54ebb 100644 --- a/admin_login.php +++ b/admin_login.php @@ -2,6 +2,14 @@ // ★ 紧急加固(2026-09-29):第二层 IP 白名单 —— 登录页本身也只对白名单 IP 开放 require_once __DIR__ . '/security.php'; secGatePage(); +// ★ 首跑顺序(2026-10-09):装库先于安全配置 —— install.php 还没跑完就先去装库。 +// 引导模式下 secGatePage 已先行 302;这里兜住的是「本机 127.0.0.1 直连」与 +// 「已引导但装库没做过」两种入口。secTokenUrl 在令牌未配置时原样返回 +// install.php,配了令牌则带上 ?token= 过第三层门禁(与 admin.php 同款拼法)。 +if (secInstallPending()) { + header('Location: ' . secTokenUrl('install.php')); + exit; +} // ★ 首次部署:管理密码还是分发占位符 → 登录页是个死胡同(要密码,可密码还不存在)。 // 改投初始化页,让部署者先把自己的第一个密码设出来。 if (!secAdminPassConfigured()) { diff --git a/install.php b/install.php index 1af11fe..7e0170e 100644 --- a/install.php +++ b/install.php @@ -465,6 +465,11 @@ ul{margin:6px 0;padding-left:20px}
+ + + +

运行模式

diff --git a/security.php b/security.php index 97e862f..cb86afe 100644 --- a/security.php +++ b/security.php @@ -588,6 +588,16 @@ function secBootRecoveryRedirect() { exit; } +/** + * 数据库是否还没装完(2026-10-09「首跑顺序」判据)。 + * 判据 = 自毁式安装页还在场上:install.php 存在 且 install.lock 不存在。 + * 装完后 install.php 自删 + lock 落地 → 恒为 false; + * 线上老站根本没有 install.php → 同样恒为 false,行为零变化。 + */ +function secInstallPending() { + return is_file(__DIR__ . '/install.php') && !is_file(__DIR__ . '/install.lock'); +} + /** * 页面级闸门。$allowBoot=true 时接受 boot 令牌解锁(引导配置页 / 2FA 页使用)。 * - 已引导 且 不在 IP 白名单 → nginx 原生 404(不是 403:403 会告诉探测者 @@ -608,6 +618,15 @@ function secGatePage($allowBoot = false) { if (!secIsBootstrapped()) { secLog('ip_gate_open_bootstrap', 'path=' . (isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '-')); $script = basename(isset($_SERVER['SCRIPT_NAME']) ? $_SERVER['SCRIPT_NAME'] : ''); + // ★ 首跑顺序(2026-10-09):装库先于安全配置 —— install.php 还没跑完 + // (文件在场、无 install.lock)时,任何引导入口一律先 302 到自毁式安装页; + // 装完它自删 + lock 落地,下一次进来才轮到 admin_2fa_setup.php。 + // 修复实录:全新测试站直接进了 2FA 引导页,数据库安装页根本没露面。 + if ($script !== 'install.php' && secInstallPending()) { + secLog('route_install_first', 'from=' . $script); + header('Location: install.php'); + exit; + } if ($script === 'admin.php') { header('Location: admin_2fa_setup.php'); exit;