博客装修记:升级 Typecho 主程序
Typecho 似乎很久没有发过正式版,甚至今年六月份的也是pre release,不过官方其实是有CI管线的,所以其实可以很轻松地下载到测试版(也就是nightly版)。
简单review了一下提交记录之后,决定把目前正在使用的测试版,升级到另一个测试版。当然,果不其然地,升炸了。

1. 前言
本站刚运行的时候,曾经给Typecho提过issues,就是给后台改进了一下Ctrl+S的实际表现,那时候实际上也是用的测试版,也就是在维护着合并这个Patch之后,直接打包下载了CI管线输出的nightly版本,因此当时的程序版本就已经是1.3.0。不过根据官方发版规划,这个所谓的1.3.0在很长一段时间内都是未正式发版的情况,所以如果还想继续使用nightly,就应该想办法触发升级机制的运作。
此外,虽然笔者之前一直遵守非必要不动程序本体,仅仅改动主题和插件的原则,但本质上来说,依然可能存在一些不兼容因素,所以必然不能直接在远端升级,免得让站点长时间宕机。比较安全的方法是先在测试环境弄好,确定好流程了,然后备份远端环境,再执行升级。
有了理论上的准备,就可以进行实操了。
2. 实际升级
以下的2.1与2.2,均在本地开发机上进行。
2.1 文件替换
按照官方的升级教程,需要下载最新测试版程序,然后删除并替换对应文件夹。不过此时进入博客后台,系统其实是检测不到版本发生变化的,因为版本号依然是1.3.0。为了触发升级,需要手动改一下系统原来的版本号。
打开数据库控制台,执行:
use 数据库名;
UPDATE `typecho_options` SET `value`='Typecho 1.3.0-old' WHERE `name`='generator' AND `user`=0;手动给原来的版本号加上old后缀(其实加什么都行,主要是发生变化)。此时进入后台,就能看见系统已经弹出了升级提示,按升级按钮让系统自己升级就行了。

一般情况下,升级过程到此结束。但既然引言中提到升炸了,事情当然没有这么简单。
2.2 错误修复
本地开发版打开了Debug选项,因此页面上可以显示到底发生了什么事情。很显然,问题出在主题文件上,因为Waxy也是一个很久没有更新的主题,出现错误也是难免的事。
定位到错误提示所在的行数:
<?php if (array_key_exists('star', unserialize($this->___fields()))): ?>
<!-- 此处略去 -->
<?php endif; ?>具体来说,是unserialize返回了false,表示反序列化失败。本地开发环境有Xdebug,在打上断点之后跟踪执行流,很快就来到了/var/Typecho/Config.php,执行完__construct就完事了,外层返回了一个Config类。
显然,Waxy主题正在假定某个内部结构不发生变化,然而正如事实所见,这个结构变化了,引发反序列化失败,也就导致页面无法加载。好在天无绝人之路,在这个类里面还可以找到公开的toArray方法,可以原样输出输出获取到的数组。因此只需要检查主题文件中的所有类似代码段,把他们改成:
<?phpif (array_key_exists('star', $this->___fields()->toArray())): ?>
<!-- 此处略去 -->
<?php endif; ?>就可以修复问题了。
2.3 正式应用与后台修补
简单测试系统各功能基本正常后,SSH登录到服务器上,按照上面的一模一样的流程操作即可。操作完后会发现后台样式出现了问题,不要慌,这是Cloudflare缓存尚未刷新导致的,只需要到CF的控制面板手动清空缓存即可恢复正常。
当然了,升级哪有一帆风顺的。这个nightly版本有一个不确定算不算问题的问题:编辑区的文件上传框,不再限制文件选择类型了,而是改成了在js中简单过滤(当然后台也有过滤)。这就导致选择文件的时候,选择框不会帮你预先过滤,显得很杂乱。
定位到用于生成检查代码的/admin/file-upload-js.php,很容易就能找到里面的接口$options->allowedAttachmentTypes,其形式是数组包含的一个个后缀名,又因为<input>标签支持accept语法来确定需要什么文件,因此可以写一点代码来生成合适的内容:
$allowFileList = '';
foreach($options->allowedAttachmentTypes as $t) {
$allowFileList .= '.'.$t.', ';
}
$allowFileList = htmlspecialchars(rtrim($allowFileList, ' \n\r\t\v\0,'));然后向下滚动到73行附近,修改生成标签的代码:
const fileInput = $('<input type="file" name="file" accept="<?php echo($allowFileList); ?>"/>').hide().insertAfter(btn);保存,问题就解决了。
3. 样式修复
后台文本编辑器的字体变得不那么好看了,但只需要替换为原来保存下来的样式就可以了:
code,pre,.mono {
font-family: 'SF Mono', Menlo, Monaco, Consolas, 'Courier New', -apple-system, system-ui, monospace;
}4. 写在最后
这次升级其实并没有什么功能上的增加,只是可以认为是安全更新。
事情起因其实是另一位博主DavidYR两年前的那篇文章,称自己站点被攻击,写入了一句话木马。简单分析发现,此漏洞符合官方提及的XSS漏洞,甚至那位博主站点被植入的代码与Issue #1545中的完全一致。虽然由于站点部署时间的关系,本站实际安装时漏洞已经被修复,但这件事也是作为一个提醒:定期进行安全更新,是有必要的。
其实这次升级也说明了一件事,建站不是运行俩脚本就能完事的,虽然搭建过程可能可以有脚本辅助一键安装,但若不了解常见攻击类型和站点运作原理,部署出来的网站其实也可能会成为攻击者的温床,或者像笔者这样,升级操作升炸了却难以修复。
(完)