页脚入口与安装向导;加固管理员口令存储
需求落实: 1. 页脚常驻「管理后台」链接,不用再手敲 admin.php。 2. 页脚「由 FeedLite 驱动」中的 FeedLite 改为超链接(指向开源仓库); 后台新增「页脚」设置组,可关闭整句(关闭后只留管理后台链接),也可改链接地址。 3. 管理员口令存储方式在后台如实展示:数据库 settings.admin.password_hash, 存 bcrypt 单向哈希(不可逆),明文不落盘。 4. 新增安装向导 install.php:data/ 下没有数据库时自动 302 引导; 体检环境 -> 填站点信息与口令 -> 生成 config.php -> 建库建表 -> 完成。 顺带修掉一个真实安全隐患: config.php 里的出厂占位口令 CHANGE_ME_admin 原先可被当作有效口令登录后台 (等于公开仓库里就写着线上后门口令)。现改为占位/空值一律拒绝, 未设口令时后台明确提示去安装向导,不再含糊报「口令不正确」。 实现要点: - 新增 fb_cfg_file():只读配置文件、不叠加 settings 表、绝不触达数据库。 「是否已安装」以数据库文件是否存在为判据,而 fb_cfg() 读 settings 会触发 fb_db() 就地建库 —— 那会把安装态自己造出来,所以判定必须走不碰库的通道。 - bootstrap 只在已安装时建库;未安装则 302 到 install.php(CLI 下报错退出)。 - fb_settings_flat() 在未安装时直接返回空,杜绝任何隐式建库。 - install.php 用文本级替换生成 config.php,保留全部注释与 OSS/SMTP 段落; 生成的 PHP 用 token_get_all(TOKEN_PARSE) 校验后才落盘。 - 页脚链接只放行 http/https,后台保存时校验(防 javascript: 伪协议)。 - style.css 页脚行改 flex,版本号 v6 -> v7。 验证: - php -l 全量通过;现有回归 25/25、附件专项 13/13 保持通过。 - 安装向导专项 60/60(含「明文口令未写入任何文件」「数据库字节里搜不到明文」)。 - 反向验证:撤销三处修复后对应断言确实失败(断言非装饰)。 - 几何检查:本机 15/15 + 页脚专项全过;线上真实 URL 15/15 + 页脚专项全过。 - 线上真机冒烟 22/22。
This commit is contained in:
+53
-3
@@ -155,6 +155,24 @@ function fb_thread_public(array $thread) {
|
||||
return fb_is_admin();
|
||||
}
|
||||
|
||||
/* ---------------- 安装状态 ---------------- */
|
||||
|
||||
/** FeedLite 开源仓库地址(页脚版权链接的默认值,可在后台改) */
|
||||
function fb_credit_url_default() {
|
||||
return 'https://code.yangyq.net/laoyang/FeedLite';
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否已安装:数据库文件存在且非空。
|
||||
* ★ 必须用 fb_cfg_file()(纯文件配置)而不是 fb_cfg():
|
||||
* 后者会去读 settings 表 -> 触发 fb_db() -> 就地建出一个空库,
|
||||
* 那样第二秒就永远进不去安装向导了(安装态会被自己"装"出来)。
|
||||
*/
|
||||
function fb_installed() {
|
||||
$p = (string)fb_cfg_file()['db_path'] ?? '';
|
||||
return ($p !== '' && is_file($p) && (int)@filesize($p) > 0);
|
||||
}
|
||||
|
||||
/* ---------------- 站点设置(数据库覆盖 config.php) ----------------
|
||||
* 后台改的设置存在 settings 表,读取时覆盖 config.php 的同名项;
|
||||
* config.php 因此变成「出厂默认值」,部署后不必再手工改它。
|
||||
@@ -164,6 +182,8 @@ function fb_thread_public(array $thread) {
|
||||
function fb_setting_keys() {
|
||||
return [
|
||||
'site_name', 'site_desc',
|
||||
'footer.show_credit', 'footer.credit_url',
|
||||
'installed_at',
|
||||
'attachment.storage',
|
||||
'upload.image_max_per_post', 'upload.image_max_bytes',
|
||||
'upload.video_max_per_post', 'upload.video_max_bytes',
|
||||
@@ -187,6 +207,10 @@ function fb_settings_flat($reload = false) {
|
||||
static $loading = false;
|
||||
if ($flat !== null && !$reload) { return $flat; }
|
||||
if ($loading) { return []; }
|
||||
/* ★ 未安装时绝不读表:读表要先 fb_db(),那会就地建出一个空数据库文件,
|
||||
* 等于把「未安装」这个状态自己抹掉 —— 安装向导就再也进不去了。
|
||||
* fb_installed() 走纯文件配置、不碰库,可以安全地在这里调用。 */
|
||||
if (function_exists('fb_installed') && !fb_installed()) { return []; }
|
||||
$loading = true;
|
||||
$out = [];
|
||||
try {
|
||||
@@ -280,19 +304,45 @@ function fb_admin_login() {
|
||||
@session_regenerate_id(true);
|
||||
}
|
||||
|
||||
/** 安装向导会直接写成对的占位值;这些值一律视为「没设口令」 */
|
||||
function fb_admin_password_placeholder() {
|
||||
return ['', 'CHANGE_ME_admin', 'CHANGE_ME', 'change_me', 'admin', 'password'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 口令当前存在哪 —— 用于在后台如实告诉管理员「口令保存在哪里、是否加密」。
|
||||
* ① 'db' :数据库 settings 表的 admin.password_hash,bcrypt 单向哈希(不可逆)
|
||||
* ② 'file' :config.php 的 admin_password 明文(仅安装向导之前的老部署会走到)
|
||||
* ③ 'none' :尚未设置(或仍是出厂占位值)→ 后台拒绝登录,只能用安装向导设一个
|
||||
*/
|
||||
function fb_admin_password_source() {
|
||||
if ((string)fb_setting('admin.password_hash', '') !== '') { return 'db'; }
|
||||
$cfg = trim((string)fb_cfg('admin_password', ''));
|
||||
if ($cfg === '' || in_array($cfg, fb_admin_password_placeholder(), true)) { return 'none'; }
|
||||
return 'file';
|
||||
}
|
||||
|
||||
/** 是否已设置过可用口令(未设置时后台不可登录,且不提示"口令不正确"而是提示去安装) */
|
||||
function fb_admin_password_is_set() {
|
||||
return fb_admin_password_source() !== 'none';
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验管理员口令。
|
||||
* 后台改过口令后存的是 password_hash;没改过就用 config.php 的 admin_password。
|
||||
* 后台改过口令后存的是 password_hash;没改过才回落到 config.php 的明文。
|
||||
* ★ 出厂占位值(CHANGE_ME_admin 等)一律拒绝:否则 clone 下来没改配置的站点,
|
||||
* 任何人都能用文档里公开的占位口令登进后台。
|
||||
*/
|
||||
function fb_admin_verify_password($pw) {
|
||||
$pw = (string)$pw;
|
||||
$hash = (string)fb_setting('admin.password_hash', '');
|
||||
if ($hash !== '') { return password_verify($pw, $hash); }
|
||||
$cfg = (string)fb_cfg('admin_password', '');
|
||||
return $cfg !== '' && hash_equals($cfg, $pw);
|
||||
if ($cfg === '' || in_array($cfg, fb_admin_password_placeholder(), true)) { return false; }
|
||||
return hash_equals($cfg, $pw);
|
||||
}
|
||||
|
||||
/** 修改管理员口令(存哈希到数据库,立即生效) */
|
||||
/** 修改管理员口令(存 bcrypt 哈希到数据库,立即生效) */
|
||||
function fb_admin_set_password($pw) {
|
||||
return fb_set_setting('admin.password_hash', password_hash((string)$pw, PASSWORD_DEFAULT));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user