DragonRster`s Void
Banner Image
目录
站点框架的缺陷分析与优化路线

本篇博文由DeepSeek-V4生成


这套 90s 静态站点框架从零搭建至今,已经支撑了十几篇文章的构建。在持续迭代中,代码逐渐暴露出了一些设计上的短板。花了一天时间做了一次彻底审查,从安全、正确性、设计、可维护性四个维度梳理了所有问题。

一、安全问题

1. 路径穿越绕过保护(web_server.py)

受保护目录检查只用 self.path.startswith('/data/') 判断,但 /cgi-bin/../../data/guestbook.txt 这类请求会绕过检查,落到 CGI 处理器后发现不是脚本,回退到静态文件服务,直接返回数据文件内容。

修复:在 _resolve_path 中规范化路径后做边界检查,拒绝任何跳转到非公开目录的请求。

2. 编辑器无任何访问控制(editor.py)

任何人都能通过 /cgi-bin/editor.py 创建、编辑、删除文章。没有登录、没有密码、没有任何验证。

修复:至少加一个 shared secret(环境变量或配置文件中的 token),通过 URL 参数或 Cookie 验证。不追求完美,但不能零门槛。

3. 留言板无速率限制(guestbook.py)

同一 IP 可以无限提交留言,没有任何时间间隔或验证码。一个简单脚本就能灌满数据文件。

修复:基于 IP 的冷却时间(60 秒)+ 可选 honeypot 隐藏字段。

4. 编辑器无 CSRF 防护(editor.py)

删除确认表单没有 CSRF token。如果有认证系统,恶意网站可以伪造删除请求。

修复:生成一次性的 session token 校验。

二、正确性 Bug

5. 搜索引擎结果强制小写(search.py 第 72 行)

snippet.lower().replace(q, ...) 把整个摘要先转为小写。中文不受影响,但英文搜索结果全部小写输出,专有名词面目全非。

修复:用正则 re.sub(q, highlight, snippet, flags=re.IGNORECASE) 保持原大小写做替换。

6. 英文归档/标签页使用中文侧边栏(generate-archive.ps1)

无论 -Lang en 参数,脚本始终加载 sidebar-left.htmlsidebar-right.html(中文版)。英文标签页写着"留言板"和"最新文章"而不是英文。

修复:根据 $Lang 参数选择对应的英文侧边栏组件。

7. 英文 latest-posts 构建顺序错误(rebuild-all.ps1)

英文博客页面在英文 latest-posts-en.html 生成之前就构建了。首次构建时英文博客侧边栏的"最新文章"会为空。第二次构建才能看到(使用的是上次的缓存文件)。

修复:把英文 archive 生成移到英文页面构建之前。

8. 留言板 email 字段 name 属性冲突(sidebar-left.html)

email 输入框同时有 name="email"name="content"。IE5.5 可能取后者,导致邮件地址覆盖留言正文。

修复:删掉多余的 name="content"

9. 脚注正则无法处理跨行定义(build.ps1)

[^\n]*? 明确排除了换行。脚注定义如果分成两段(第二段不是新的定义),后半段会丢失。

修复:改为 (?:(?!).)*? + Singleline 模式,让脚注内容可以跨行。

10. 草稿检测误判(build.ps1)

$content -match '' 在全文搜索。如果文章正文中正好包含了这段注释(比如这篇 build-script 博文,或者技术教程里引用草稿相关的代码示例),就会被当成草稿跳过。

修复:只检测文件前 N 个字符或前 3 行,而不是全文搜索。

11. 编辑时标题重复注入(editor.py)

编辑已有文章时,标题替换正则遇到"已经替换过的标题"就匹配不上,但 else 分支又会再套一层标题,结果出现双标题。

修复:检测是否已经存在标题 heading(而非依赖"标题"占位符),存在则替换文本内容,不存在则新增。

12. 搜索索引分隔符冲突(generate-archive.ps1)

--- 分隔索引条目,但文章正文中完全可能包含三个连续的破折号,导致搜索索引解析错乱。

修复:使用更不容易冲突的分隔符,比如 ===END===,或者改用固定的长度前缀。

三、设计缺陷

13. 页面布局代码在 5+ 处重复

完整的 HTML 页面拼装(header + banner + table + sidebars + footer)在 build.ps1、generate-archive.ps1、editor.py 的三个页面渲染函数中各自实现。改一处布局(比如调整表格宽度、加导航元素)就需要改 5 个地方。

修复:将页面骨架抽取到一个共享模板文件(如 src/components/page-shell.html),所有组件通过占位符注入。

14. CGI 脚本大量重复代码

parse_form_data() 在 editor.py 和 guestbook.py 各有一份实现。get_client_ip() 的 guestbook 版本更完整(多了 X-Real-IP 回退),但 editor 没有。rebuild 触发逻辑在 editor.py 和 guestbook.py 各写了一遍。

修复:创建 cgi-bin/_common.py 共享模块,所有 CGI 脚本从它导入公共函数。

15. 没有配置文件

站点名称、域名、端口、最大留言数、版权年份等散布在 7+ 个文件中。换一个部署环境就需要改多处。

修复:创建 config.psd1(PowerShell 格式)或 config.json,build 脚本和 CGI 脚本统一读取。

16. 英文搜索始终搜中文索引

search.py 硬编码加载 search_index.txt,不检查 search_index_en.txt。也没有 lang 参数。英文页面用户搜索时得到的是中文结果。

修复:增加 lang 查询参数,根据语言加载对应索引文件。

17. 没有增量构建

每次 rebuild-all.ps1 重新构建所有页面。10 篇文章只需几秒,但 100 篇就会成比例增长。绝大多数页面没有变化却也要重新拼接、替换、写出。

修复:利用内容文件的 MD5 哈希或修改时间来判断是否需要重新构建,只更新有变化的页面。

18. 博客脚本加载不区分页面类型

build.ps1 无论构建什么页面都会把博客侧边栏(sidebar-left-blog.html 等)和 TOC 模板(toc-item.html 等)加载进来。构建首页时这些文件完全用不到却白白读了磁盘。

修复:把这些 Get-Content 移到 if ($PageName -like "blog-*") 分支内部。

19. 留言板和更新日志存在竞争条件

guestbook.py 用 open(file, 'a') 追加写入,build.ps1 用 Get-Content 读取。同时操作时可能读到不完整的行或导致构建失败。

修复:写入时先写到临时文件,然后原子性 rename。或者使用文件锁。

20. 数据文件无备份策略

guestbook.txt 和 changelog.txt 都是单文件、纯追加。文件损坏意味着所有历史数据丢失。

修复:每次修改前自动备份到带时间戳的副本,或者用 Git 作为数据版本控制。

四、可维护性

21. 路径分隔符硬编码反斜杠

PowerShell 脚本中到处是 src\components\header.html 这样的硬编码反斜杠路径。在 Linux 上运行 PowerShell Core 会找不到文件。

修复:统一用 Join-Path 拼接路径,或者用正斜杠(PowerShell 会自动处理)。

22. CSS 在 CGI 脚本中内联了三遍

editor.py 的 render_page()render_list()render_delete_confirm() 各自内联了一份几乎相同的 CSS。改一个颜色需要改三处。

修复:抽取到共享的 HTML 模板片段或 CSS 变量函数中。

23. 硬编码的域名和 URL

https://www.dragonrster.cn/dist/rss.xml 和 "DragonRS Void" 在侧边栏、编辑器、构建脚本中硬编码。部署到其他域名时需要全局搜索替换。

修复:由配置文件统一管理。

24. 构建脚本缺少 try/catch

整个 build.ps1 没有错误处理。如果 guestbook.txt 正被写入导致读取失败、磁盘满了写不出文件、内容文件编码损坏读不了,构建就直接崩溃。

修复:关键 I/O 操作(读写文件、扫描目录)加 try/catch,记录到构建日志而非静默失败。

25. rebuild-all.ps1 强制修改执行策略

Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process 在加锁的机器上会直接失败,即使 caller 已经正确地传了 -ExecutionPolicy 参数。

修复:去掉这行,依赖调用方(build.cmd)传参数。

26. 没有输入尺寸限制(editor.py)

标题、标签、正文都没有最大长度检查。恶意 POST 可以耗尽服务器内存。

修复:标题限制 200 字符,正文限制 500KB。

27. 日志压缩逻辑有缺陷(web_server.py)

如果旧日期的日志文件被重新创建(比如服务器重启写入了老日期),压缩函数发现 .gz 已存在就跳过,新旧日志混在一起处理不了。

修复:检测到冲突时追加到压缩文件而非跳过。

28. 缺少部署脚本

没有将 dist/ 部署到生产服务器的手段。每次手动复制粘贴。

修复:添加 scripts/deploy.ps1,使用 rsync/scp。

五、优先修复路线

不可能一次性修完所有问题。建议分三批:

第一批(本周):安全 + 关键 Bug

  • 路径穿越修复
  • 编辑器 token 认证
  • 留言板速率限制
  • email 字段冲突修复
  • 英文最新文章构建顺序修复
  • 草稿检测误判修复
  • 第二批(本月):设计改进

  • 抽取共享模块(_common.py)
  • 创建配置文件(config.json)
  • 页面骨架模板消除代码重复
  • CSS 抽取
  • 英文搜索接入
  • 第三批(后续):工程完善

  • 增量构建
  • 部署脚本
  • 数据文件备份
  • 输入校验与错误处理
  • 路径分隔符统一
  • 小结

    这套框架在功能上是完整的——能写文章、有留言板、有搜索、支持中英文——但是代码质量处于典型的"原型阶段"。问题集中在三块:安全防护缺失(路径穿越、无认证、无速率限制),代码重复严重(布局、工具函数、配置),以及边界条件处理不足(错误处理、竞争条件、备份)。

    值得肯定的是,框架没有外部依赖,所有功能都在 200KB 以内的纯 Python + PowerShell 实现。架构思路清晰——组件化拼接 + 数据注入——只是执行上还缺一次工程化整理。

    接下来的改进重点是安全先行、抽取共享模块、引入配置文件。这是从"能用的原型"到"可靠的工具"的必经之路。


    « 脚本详解:build.ps1 返回主页
    English

    搜索

    最新文章

    » 站点框架的缺陷分析与...
    » 脚本详解:build.ps1
    » 脚本详解:gener...
    » 脚本详解:gener...
    » 脚本详解:rebui...

    » 文章归档


    本文标签

    网站建设 教程 更新日志


    功能

    » RSS 订阅
    » GitHub 源码
    » 返回顶部
    » 文章归档


    DRAGONRSTER
    CC BY-NC-SA
    © 2004-2026 DragonRster • Made with HTML • 本站支持IE5.5+
    最佳浏览分辨率:1024x768 • 最后更新于 2026年04月29日 14:19:33