perf/security: V0.9.5-0.9.6 db_pending_upgrades 缓存 + config 凭据外置

- V0.9.5 (P3): db_pending_upgrades() 加 static 请求内缓存,同请求只算一次
- V0.9.6 (安全): config.php 移除明文密码,改从 gitignored 的 config.secret.php 或
  环境变量读取;新增 config.secret.php.example 模板;.gitignore 忽略 config/config.secret.php
  (修复 Webhook reset 反复覆盖明文凭据的隐患;服务器部署前需自建 config.secret.php)
This commit is contained in:
2026-08-09 00:34:54 +08:00
parent d5c1edefae
commit 6d3962574c
4 changed files with 38 additions and 7 deletions
+1
View File
@@ -3,6 +3,7 @@ storage/cache/
storage/data/ storage/data/
storage/logs/ storage/logs/
storage/uploads/ storage/uploads/
config/config.secret.php
*.zip *.zip
_dbg_login.html _dbg_login.html
start_local.sh start_local.sh
+8 -3
View File
@@ -544,12 +544,15 @@ if (!function_exists('site_url')) {
*/ */
function db_pending_upgrades(): int function db_pending_upgrades(): int
{ {
// V0.9.5:请求内缓存(一次请求内多次调用只算一次),避免每后台页重复 glob+md5_file+SELECT
static $cache = null;
if ($cache !== null) return $cache;
try { try {
if (\Core\Db::driver() !== 'mysql') return 0; if (\Core\Db::driver() !== 'mysql') { $cache = 0; return 0; }
$dir = db_upgrade_dir(); $dir = db_upgrade_dir();
if (!is_dir($dir)) return 0; if (!is_dir($dir)) { $cache = 0; return 0; }
$files = glob($dir . '/*.sql') ?: []; $files = glob($dir . '/*.sql') ?: [];
if (!$files) return 0; if (!$files) { $cache = 0; return 0; }
\Core\Installer::ensureUpgradeLog(); \Core\Installer::ensureUpgradeLog();
$applied = \Core\Db::query("SELECT file, hash FROM db_upgrades")->fetchAll(\PDO::FETCH_KEY_PAIR); $applied = \Core\Db::query("SELECT file, hash FROM db_upgrades")->fetchAll(\PDO::FETCH_KEY_PAIR);
$n = 0; $n = 0;
@@ -560,8 +563,10 @@ if (!function_exists('site_url')) {
$n++; $n++;
} }
} }
$cache = $n;
return $n; return $n;
} catch (\Throwable $e) { } catch (\Throwable $e) {
$cache = 0;
return 0; return 0;
} }
} }
+20 -4
View File
@@ -1,8 +1,24 @@
<?php <?php
/** /**
* 全局配置 * 全局配置(不含敏感凭据,可安全入库)
* driver: 'file' 开箱即用(无需数据库);'mysql' 用于生产部署 *
* 敏感凭据(数据库密码 / 后台密码)从以下优先级读取,避免明文写入仓库:
* 1) 同级 config.secret.phpgitignored,需服务器侧自建;git reset --hard 不会被清除,建一次即可)
* 2) 环境变量 COOLCOTH_DB_PASS / COOLCOTH_ADMIN_PASS
* 3) 本文件中的占位(为空 → 连接失败,仅作本地演示占位)
*
* 部署步骤(务必在下次 Webhook 部署前完成,否则 DB 连接 / 后台登录会失败):
* 在服务器 /www/wwwroot/coolcoth.com/config/ 下新建 config.secret.php,内容参考 config.secret.php.example
*/ */
$secret = [];
if (file_exists(__DIR__ . '/config.secret.php')) {
$secret = require __DIR__ . '/config.secret.php';
}
$secret = array_merge([
'db_pass' => '',
'admin_pass' => '',
], $secret);
return [ return [
'app' => [ 'app' => [
'name' => '酷冰甲 · 降温服', 'name' => '酷冰甲 · 降温服',
@@ -17,7 +33,7 @@ return [
'port' => 3306, 'port' => 3306,
'dbname' => 'coolcoth_comf', 'dbname' => 'coolcoth_comf',
'user' => 'coolcoth_comf', 'user' => 'coolcoth_comf',
'pass' => 'QDBKzY817hDhTiKk', 'pass' => $secret['db_pass'] ?: (getenv('COOLCOTH_DB_PASS') ?: ''),
'charset' => 'utf8mb4', 'charset' => 'utf8mb4',
], ],
'file' => [ 'file' => [
@@ -27,7 +43,7 @@ return [
// 后台登录账号(文件模式 / 首次安装) // 后台登录账号(文件模式 / 首次安装)
'admin' => [ 'admin' => [
'username' => 'admin', 'username' => 'admin',
'password' => 'admin888', 'password' => $secret['admin_pass'] ?: (getenv('COOLCOTH_ADMIN_PASS') ?: ''),
// 本地规则红线:生产环境(mysql 模式)必须关闭配置文件兜底账号,禁用默认密码后门。 // 本地规则红线:生产环境(mysql 模式)必须关闭配置文件兜底账号,禁用默认密码后门。
// 仅本地演示(file 模式)可临时置 true。远端已实测默认密码可登录,已置 false 封堵。 // 仅本地演示(file 模式)可临时置 true。远端已实测默认密码可登录,已置 false 封堵。
'allow_config_fallback' => false, 'allow_config_fallback' => false,
+9
View File
@@ -0,0 +1,9 @@
<?php
/**
* config.secret.php 模板(可入库,供复制参考)
* 复制为 config.secret.php 并填入真实凭据;config.secret.php 已被 .gitignore 忽略,不会入库。
*/
return [
'db_pass' => '在此填入数据库密码',
'admin_pass' => '在此填入后台密码(务必改为强口令,不要用 admin888',
];