## 装完自删
install.php 是可写文件,留着等于长期暴露一个「重装入口」。
现在安装成功后自动 unlink 自己,不再要用户手动删。
两种结果都如实告知,不假装:
- 删除成功 → 成功页显示「已自动删除」
- 删不掉 → 显示「没能自动删除」+ 具体原因(目录不可写 / 属主不符)+ 手工处理命令
安装本身不受影响:清理失败不算安装失败。
## 🔴 修掉一个越权动作(本轮最要紧的修改)
初版为了「尽量删掉」,先 @chmod(dirname($self), 0755) 再 unlink。
这等于程序自己把「站点根只读」这个安全设置拆掉了 —— 管理员有意设成只读的目录
被 PHP 改回可写,然后文件就被删了。既违反管理员意图,也让「删不掉」这个真实场景
永远测不出来(我第一版测试就是这么被骗过去的)。
删除失败就如实报告,不准硬删。
## 站点根只读不再阻断安装
原先环境体检把「站点根可写」列为硬性要求,只读就红叉、禁止提交。
但这没道理:config.php 写不进去时,设置会全部落到数据库 settings 表,功能一样。
反过来,站点根只读是更安全的做法。
现在 data/ 与 uploads/ 可写仍是硬要求,站点根只读只显示为提示(不影响判定)。
## rename 兜底收窄
只在目录可写时才试 rename(目录不可写时 rename 同样失败,
硬试会在站点里留下 .deleted.xxxx 垃圾)。改名成功但删不掉则还原。
验证:
- 自删 A 场景(目录可写)→ 文件已删,页面提示「已自动删除」
- 自删 B 场景(目录只读)→ 文件保留,提示「目录对 Web 用户不可写」,安装仍成功
- 全新安装 33/33、老基线 25/25、附件专项 22/22、安装向导 60/60
This commit is contained in:
+87
-9
@@ -44,8 +44,10 @@ function fb_install_checks() {
|
|||||||
],
|
],
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// 只有 data/ 与 uploads/ 是硬要求(数据库和附件必须能写)。
|
||||||
|
// 站点根不必可写:config.php 写不进去时,设置会落到数据库 settings 表,
|
||||||
|
// 站点照常运行。反过来,把站点根设成只读是更安全的做法,不该被判为错误。
|
||||||
$dirs = [
|
$dirs = [
|
||||||
FB_ROOT => '站点根目录(要写入 config.php)',
|
|
||||||
FB_ROOT . '/data' => '数据库目录 data/',
|
FB_ROOT . '/data' => '数据库目录 data/',
|
||||||
FB_ROOT . '/uploads' => '本机附件目录 uploads/(本机存储模式用)',
|
FB_ROOT . '/uploads' => '本机附件目录 uploads/(本机存储模式用)',
|
||||||
];
|
];
|
||||||
@@ -82,13 +84,25 @@ function fb_install_checks() {
|
|||||||
$rows[] = ['目录可写 ' . $shown, $w ? '可写' : '不可写', $w, $why, $w ? '' : ('请给 Web 用户写权限,例如 chown www:www ' . $d)];
|
$rows[] = ['目录可写 ' . $shown, $w ? '可写' : '不可写', $w, $why, $w ? '' : ('请给 Web 用户写权限,例如 chown www:www ' . $d)];
|
||||||
}
|
}
|
||||||
|
|
||||||
// config.php 若已存在(例如从模板复制过),必须可写才能就地更新
|
// 站点根:只提示,不阻断。
|
||||||
|
// 写不进 config.php 时,站点设置会全部落到数据库 settings 表,功能不受影响。
|
||||||
|
$rootW = is_writable(FB_ROOT);
|
||||||
|
$rows[] = [
|
||||||
|
'目录可写(站点根)',
|
||||||
|
$rootW ? '可写' : '只读(不影响使用)',
|
||||||
|
true, // 不参与 $ok 判定
|
||||||
|
'用于生成 config.php;只读时设置改存数据库,功能一样',
|
||||||
|
$rootW ? '' : '站点根只读是可接受的(更安全)。若希望生成 config.php,再给它写权限。',
|
||||||
|
];
|
||||||
|
|
||||||
|
// config.php 若已存在(例如从模板复制过),只读也不影响运行,只影响能否就地更新
|
||||||
if (is_file(FB_ROOT . '/config.php')) {
|
if (is_file(FB_ROOT . '/config.php')) {
|
||||||
$w = is_writable(FB_ROOT . '/config.php');
|
$w = is_writable(FB_ROOT . '/config.php');
|
||||||
if (!$w) { $ok = false; }
|
$rows[] = ['config.php 可写', $w ? '可写' : '只读(不影响使用)', true,
|
||||||
$rows[] = ['config.php 可写', $w ? '可写' : '不可写', $w, '需要就地更新配置', $w ? '' : '请给该文件写权限,例如 chown www:www config.php'];
|
'需要就地更新配置时才有用',
|
||||||
|
$w ? '' : '只读可接受:后台改的设置会存数据库,不写这个文件。'];
|
||||||
} else {
|
} else {
|
||||||
$rows[] = ['config.php', '尚不存在(将自动生成)', true, '由安装向导创建', ''];
|
$rows[] = ['config.php', '尚不存在(将自动生成)', true, '由安装向导创建(站点根只读时会跳过)', ''];
|
||||||
}
|
}
|
||||||
|
|
||||||
$ex = is_file(FB_ROOT . '/config.example.php');
|
$ex = is_file(FB_ROOT . '/config.example.php');
|
||||||
@@ -231,9 +245,62 @@ if (isset($_POST['do_install'])) {
|
|||||||
|
|
||||||
$done = true;
|
$done = true;
|
||||||
$GLOBALS['__fb_install_pw_set'] = $pwSet;
|
$GLOBALS['__fb_install_pw_set'] = $pwSet;
|
||||||
|
|
||||||
|
/* ---- 装完自动删除本文件 ----
|
||||||
|
* 安装向导本身是可写文件,留着等于长期暴露一个"重装入口"。
|
||||||
|
* 虽然已安装态会拒绝重装,但最干净的做法是装完即自毁。
|
||||||
|
*
|
||||||
|
* 删除失败不算安装失败:站点已经装好了,只是清理动作没成。
|
||||||
|
* 失败原因通常是文件属主不是 Web 用户(部署账号上传的文件常属 btops),
|
||||||
|
* 这时把实际情况记到 $GLOBALS 里,成功页如实告知,不假装删掉了。
|
||||||
|
*/
|
||||||
|
$GLOBALS['__fb_install_selfdel'] = fb_install_self_delete();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 安装完成后删除自身。
|
||||||
|
* @return string '' = 已删除;非空 = 没删掉的原因(给成功页显示)
|
||||||
|
*/
|
||||||
|
function fb_install_self_delete() {
|
||||||
|
$self = __FILE__;
|
||||||
|
if (!is_file($self)) { return ''; } // 已经不在了
|
||||||
|
|
||||||
|
/* ① 直接尝试删除。
|
||||||
|
*
|
||||||
|
* 🔴 这里绝不能先 chmod 目录!曾经写成 @chmod(dirname($self), 0755) 再删,
|
||||||
|
* 等于程序自己把"站点根只读"这个安全设置拆掉了 —— 用户有意设成只读的目录
|
||||||
|
* 被 PHP 改回可写,然后文件就被删掉了。既违反管理员意图,也让"删不掉"
|
||||||
|
* 这个真实场景永远测不出来。
|
||||||
|
* 删不掉就如实报告,不要硬删。
|
||||||
|
*/
|
||||||
|
if (@unlink($self)) { return ''; }
|
||||||
|
|
||||||
|
// ② 兜底:只有在目录可写时才试 rename(目录不可写时 rename 同样会失败,
|
||||||
|
// 硬试只会在站点里留下 .deleted.xxxx 垃圾文件)
|
||||||
|
if (is_writable(dirname($self))) {
|
||||||
|
$tmp = $self . '.deleted.' . substr(md5((string)time() . random_int(1000, 9999)), 0, 8);
|
||||||
|
if (@rename($self, $tmp)) {
|
||||||
|
if (@unlink($tmp)) { return ''; }
|
||||||
|
// 改名成功但删不掉:还原,别留垃圾
|
||||||
|
@rename($tmp, $self);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ③ 都没成,给出可操作的原因
|
||||||
|
$dir = dirname($self);
|
||||||
|
$uid = function_exists('posix_getuid') ? posix_getuid() : -1;
|
||||||
|
$own = @fileowner($self);
|
||||||
|
$msg = '自动删除失败';
|
||||||
|
if (!is_writable($dir)) {
|
||||||
|
$msg .= ':' . $dir . ' 目录对 Web 用户不可写';
|
||||||
|
}
|
||||||
|
if ($uid >= 0 && $own !== false && $own !== $uid) {
|
||||||
|
$msg .= ':文件属主(uid ' . (int)$own . ')与运行用户(uid ' . $uid . ')不同';
|
||||||
|
}
|
||||||
|
return $msg;
|
||||||
|
}
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="zh-CN">
|
<html lang="zh-CN">
|
||||||
@@ -296,10 +363,21 @@ if (isset($_POST['do_install'])) {
|
|||||||
<?php if ($err !== ''): ?><?php fb_flash_box($err, 'error'); ?><?php endif; ?>
|
<?php if ($err !== ''): ?><?php fb_flash_box($err, 'error'); ?><?php endif; ?>
|
||||||
|
|
||||||
<div class="card-form" style="margin:14px 0">
|
<div class="card-form" style="margin:14px 0">
|
||||||
<p class="muted small" style="margin:0">
|
<?php
|
||||||
<b>建议:装好后把 <code>install.php</code> 从服务器删掉。</b>
|
$selfDel = isset($GLOBALS['__fb_install_selfdel']) ? (string)$GLOBALS['__fb_install_selfdel'] : '';
|
||||||
它在已安装状态下会拒绝重装,但删掉更干净。
|
?>
|
||||||
</p>
|
<?php if ($selfDel === ''): ?>
|
||||||
|
<p class="muted small" style="margin:0">
|
||||||
|
<b>✅ <code>install.php</code> 已自动删除</b>,不会再有重复安装的入口。
|
||||||
|
</p>
|
||||||
|
<?php else: ?>
|
||||||
|
<p class="muted small" style="margin:0">
|
||||||
|
<b>⚠️ 安装已完成,但 <code>install.php</code> 没能自动删除。</b><br>
|
||||||
|
原因:<?php echo e($selfDel); ?>。<br>
|
||||||
|
它在已安装状态下会拒绝重装,但建议手工删掉,或在服务器上执行:<br>
|
||||||
|
<code>sudo chown www:www <?php echo e(__DIR__); ?>/install.php && sudo rm <?php echo e(__DIR__); ?>/install.php</code>
|
||||||
|
</p>
|
||||||
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p>
|
<p>
|
||||||
|
|||||||
Reference in New Issue
Block a user