deployment-analysis

方案 3 部署分析:云服务器 + 域名 + HTTPS

目标:把云账本放到云服务器上,手机在任何网络下都能通过 https://你的域名 使用。
本文覆盖架构调整、安全、成本拆分、上线时间与落地步骤。
价格与时间为估算,以实际购买时为准。


一、目标架构

                     ┌─────────────────────────────────────────┐
   手机 / 电脑         │  云服务器(Ubuntu 24.04 LTS)             │
        │            │                                         │
        │ HTTPS 443  │  ┌───────────────┐                      │
        └───────────►│  │ Caddy         │  TLS 终止 + 自动证书  │
                     │  │ (80 / 443)    │  安全响应头 + 压缩     │
                     │  └───────┬───────┘                      │
                     │          │ 反向代理 127.0.0.1:51194     │
                     │  ┌───────▼───────┐                      │
                     │  │ Node 应用      │  只监听回环,不对外    │
                     │  │ (systemd)     │                      │
                     │  └───────┬───────┘                      │
                     │          │                              │
                     │  ┌───────▼───────┐   ┌───────────────┐  │
                     │  │ SQLite (WAL)  │──►│ 每日备份       │  │
                     │  │ data/book.db  │   │ + 异地同步     │  │
                     │  └───────────────┘   └───────────────┘  │
                     └─────────────────────────────────────────┘

关键原则:应用端口绝不对外暴露。 外部只能经 443 进来,应用只监听 127.0.0.1


二、架构调整

2.1 必须改的(不改就有实际风险)

# 改动 涉及文件 不改的后果 工作量
1 不再无条件信任 X-Forwarded-For,新增 ACCT_TRUST_PROXY 开关,且取最后一个转发值 server/http-util.jsserver/config.js 攻击者每次请求伪造一个 IP,登录失败限流完全失效,可无限猜密码;审计日志 IP 全是假的 30 min
2 去掉 URL 查询参数里的令牌,下载改为「一次性短时票据」或 fetch + Blob server/api.jsserver/auth.jspublic/js/api.js 令牌会写进浏览器历史、反向代理访问日志、Referer 头,等于把 30 天有效的钥匙到处撒 60 min
3 安全响应头X-Frame-Options: DENYReferrer-PolicyContent-Security-Policy、HTTPS 下加 Strict-Transport-Security server/index.js 可被 iframe 点击劫持;XSS 可外联;协议降级无提示 30 min
4 CORS 从 * 收紧:默认同源不发头,跨域需显式白名单 server/index.jsserver/config.js 任意网站都能带着用户令牌调用接口 15 min
5 应用只监听回环ACCT_HOST=127.0.0.1 部署配置 攻击者可直连 51194 绕过 HTTPS 5 min
6 服务器时区设为 Asia/Shanghai 部署配置 服务器默认 UTC,「今天」会差 8 小时,记账日期可能落到前一天 5 min

第 6 条很容易被忽略,但这个应用大量使用「本地今天/本月」,时区不设对会导致跨零点记账记到昨天。

2.2 建议改的(提升公网安全性)

# 改动 说明 工作量
7 密码强度可配,默认公网 ≥12 位 现在最低 6 位,公网太弱 20 min
8 登录失败加全局阈值 现在只按 IP 限流;再加「全局 10 分钟内失败 N 次则锁定」,防分布式轮换 IP 40 min
9 强制 HTTPS 跳转开关 ACCT_FORCE_HTTPS Caddy 也会跳,应用层再加一层保险 15 min
10 启动自检告警 若监听地址不是回环且未启用代理信任,启动时打印醒目警告 15 min
11 会话令牌轮换 每次登录作废同设备的旧令牌(可选) 30 min

2.3 部署文件(新增)

文件 作用
deploy/yunzhangben.service systemd unit:开机自启、崩溃自动重启、专用用户运行
deploy/Caddyfile 反向代理 + 自动申请续期 Let's Encrypt 证书 + 安全头
deploy/setup-server.sh 服务器初始化:建用户、装 Node 24、防火墙、时区
deploy/backup-remote.sh 每日备份 + rclone 同步到对象存储
deploy/README.md 逐步操作手册(含命令)

2.4 明确不需要改的

  • 不需要换数据库。单用户场景 SQLite 比 Postgres 更合适:无需额外服务、更快、备份就是一个文件。WAL 模式已开启。
  • 不需要 Docker(可选)。单进程 Node 用 systemd 直接跑更简单、更省资源。
  • 不需要负载均衡/多实例。单用户流量极低。
  • 不需要改前端(除下载令牌那一处)。前端是纯静态资源,经 Caddy 直接吐出去,还更快。
  • 不需要 CDN。首屏约 200 KB,3 Mbps 带宽足够。真要用 Cloudflare,记得设 Full (strict)。

2.5 服务器规格建议

建议 说明
CPU 1 核起,2 核更稳 单进程 Node,几乎不吃 CPU
内存 1 GB 起,2 GB 舒适 Node + SQLite 常驻约 60~120 MB
磁盘 20 GB 起 数据库一年也就几十 MB,主要留给备份和系统
带宽 3 Mbps 起 首屏 200 KB,3 Mbps 约 1.5 秒
月流量 1 TB 套餐绰绰有余 实际每月几百 MB
地域 香港 / 新加坡 / 日本(免备案) 见成本章节

三、安全

3.1 现状盘点(代码已有的)

状态
口令散列:scrypt(N=16384, r=8, p=1)+ 随机盐 ✅ 已做
口令校验用 timingSafeEqual 防时序攻击 ✅ 已做
会话令牌:32 字节随机(256 位),只存 SHA-256 散列 ✅ 已做
会话 30 天过期,可远程吊销、可查看登录设备 ✅ 已做
登录失败限流(同 IP 10 分钟 10 次) ⚠️ 可被伪造 IP 绕过(见 2.1 #1)
审计日志(谁、何时、做了什么) ✅ 已做
静态资源路径穿越防护 ✅ 已做
数据库 WAL + 事务 + 恢复前自动快照 ✅ 已做
自动备份 + 保留策略 ✅ 已做(仅本地)
X-Forwarded-For 处理 ❌ 无条件信任(见 2.1 #1)
安全响应头 ❌ 无(见 2.1 #3)
CORS ❌ 默认 *(见 2.1 #4)
令牌传输 ⚠️ 支持 URL 查询参数(见 2.1 #2)
密码强度 ⚠️ 最低 6 位
HTTPS ⚠️ 支持自签证书,公网应交给 Caddy

3.2 网络层

# 只开 22 / 80 / 443,其余全部拒绝
ufw default deny incoming
ufw default allow outgoing
ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw enable
  • 云厂商安全组要单独配(很多人只配了 ufw 忘了安全组)
  • SSH 加固:禁用密码登录、只用密钥、PermitRootLogin no、装 fail2ban
  • 应用端口 51194 既不开安全组也不开 ufw

3.3 传输层

  • Caddy 自动申请并续期 Let's Encrypt 证书(零配置)
  • 强制 HTTPS + HTTP/2;Strict-Transport-Security: max-age=31536000; includeSubDomains

    ⚠️ HSTS 一旦下发就难以回退(浏览器会记住一年),确认域名稳定后再开

  • TLS 1.2 起,Caddy 默认已禁用弱协议与弱套件

3.4 应用层

  • 强密码:≥12 位,建议用密码管理器生成
  • 登录限流双层:按 IP + 全局阈值
  • 令牌不进 URL(2.1 #2)—— 这条在公网环境里价值最高
  • CSPdefault-src 'self'; img-src 'self' data:; style-src 'self' 'unsafe-inline'; connect-src 'self'; frame-ancestors 'none'

    style-src 需要 'unsafe-inline',因为界面用 JS 设置内联样式。若要彻底去掉,需要改成 CSS 类,工作量较大,收益有限。

  • 不要在浏览器里保存密码:这是个人财务 App,建议每次手动输入或用密码管理器

3.5 数据层

  • data/ 目录权限 0700,数据库文件 0600,属主为专用运行用户
  • 每日自动备份(应用内已做,默认保留 14 份)
  • 异地备份rclone 同步到对象存储(阿里云 OSS / 腾讯云 COS / Backblaze B2)
    • 可选加密:rclone crypt 远程加密
    • 保留策略:本地 14 份、异地 30 份
  • 上线前做一次恢复演练:从异地备份恢复一遍,确认真的能用

3.6 运维层

做法
进程守护 systemd,Restart=always,开机自启
运行用户 专用非 root 用户(如 yzb
日志 应用日志走 journald;Caddy 访问日志单独文件 + 轮转
系统更新 unattended-upgrades 自动装安全补丁
可用性监控 UptimeRobot 免费版,5 分钟一次,挂了发邮件
证书到期 Caddy 自动续期,监控里一起看

3.7 上线后检查清单


四、成本拆分

4.1 方案 A:香港 / 海外节点(推荐,免备案)

项目 规格 首年 续费
云服务器 1C1G / 20G / 1T 流量 ¥110 ~ ¥250 同左
域名 .xyz / .top 首年促销 ¥10 ~ ¥30 ¥60 ~ ¥90
SSL 证书 Let's Encrypt ¥0 ¥0
异地备份 对象存储 5 GB ¥0 ~ ¥10 同左
合计 约 ¥120 ~ ¥290/年 约 ¥170 ~ ¥340/年

优点:无需备案,买完当天可用;国内访问延迟约 30~80 ms,完全够用。
缺点:部分海外机房晚高峰可能波动;数据出境(个人记账数据,风险可接受)。

4.2 方案 B:中国大陆节点

项目 规格 首年 续费
轻量应用服务器 2C2G / 3~5 Mbps ¥99 ~ ¥150(新用户) ¥600 ~ ¥1000
域名 .cn / .com ¥30 ~ ¥70 同左
ICP 备案 免费,但需 7~20 个工作日 ¥0
域名实名认证 免费 ¥0
SSL 证书 Let's Encrypt ¥0 ¥0
合计 约 ¥130 ~ ¥220/年(首年) 约 ¥660 ~ ¥1070/年

优点:国内访问最快(10~30 ms);合规。
缺点

  • 必须备案,7~20 个工作日,且需要域名实名 + 大陆身份证
  • 未备案时 80/443 会被云厂商封,只能用非标准端口(如 :8443),体验差且部分网络会拦
  • 续费价格通常是首年的 4~10 倍
  • 备案后内容受监管

4.3 隐性成本

估算
首次部署时间 3~4 小时(含我的代码改动)
备案时间 7~20 个工作日(仅大陆方案)
日常运维 每月 10~30 分钟(看备份、装更新)
恢复演练 每季度 30 分钟
我的代码改动 约 3~4 小时(含测试)

4.4 省钱技巧

  • 域名和服务器分开买:域名在便宜的注册商(Namesilo / Cloudflare),服务器用促销
  • 服务器买 1 年不买 3 年,先验证是否真的长期用
  • 对象存储用免费额度(Backblaze B2 有 10 GB 免费)
  • 不用买付费 SSL,Let's Encrypt 足够

五、上线时间表

5.1 走香港节点(推荐路径)

阶段 内容 谁做 耗时
1 代码安全加固(2.1 的 6 项 + 2.2 的 4 项) 3 h(含测试)
2 写部署文件与手册 1 h
3 买服务器 + 域名 + 解析 DNS 20 min
4 服务器初始化(跑 setup-server.sh 20 min
5 上传代码 + 配置 + 起服务 + Caddy 签证书 你(我指导) 30 min
6 迁移数据(把本地 book.db 传上去) 你(我指导) 15 min
7 异地备份 + 监控 + 上线检查清单 你(我指导) 40 min
合计 约 6 小时,其中你只需约 2 小时,当天可上线

5.2 走大陆节点

在 5.1 基础上 + 7~20 个工作日备案,且阶段 6 之前要等备案通过。

5.3 里程碑

D0  上午   我完成代码加固 + 部署文件(你无需参与)
D0  下午   你买服务器/域名 → 初始化 → 部署 → 数据迁移 → 上线
D0  晚上   异地备份 + 监控 + 检查清单走完
D1~D7      观察一周:可用性、速度、备份是否正常
D7         决定是否长期使用 / 是否补充固定资产等数据

六、落地步骤(命令级)

6.1 服务器初始化

# 1) 时区(很重要,否则「今天」会错)
sudo timedatectl set-timezone Asia/Shanghai

# 2) 建专用用户
sudo adduser --disabled-password --gecos "" yzb
sudo mkdir -p /opt/yunzhangben && sudo chown yzb:yzb /opt/yunzhangben

# 3) 装 Node 24
curl -fsSL https://deb.nodesource.com/setup_24.x | sudo -E bash -
sudo apt install -y nodejs

# 4) 防火墙
sudo ufw default deny incoming && sudo ufw allow 22,80,443/tcp && sudo ufw enable

6.2 上传代码与数据

# 本地执行(不含 node_modules,本项目本来也没有)
scp -r server public tools start.sh package.json README.md yzb@服务器IP:/opt/yunzhangben/

# 迁移账本:先用界面上的「设置 → 下载数据库」导出(会先做 WAL checkpoint)
scp book-2026xxxx.db yzb@服务器IP:/opt/yunzhangben/data/book.db

⚠️ 直接拷 book.db 而不做 checkpoint,可能丢掉还在 WAL 里的最新数据。
用应用提供的「下载数据库」最稳。

6.3 起服务

sudo cp deploy/yunzhangben.service /etc/systemd/system/
sudo systemctl daemon-reload && sudo systemctl enable --now yunzhangben
sudo systemctl status yunzhangben

6.4 反向代理与证书

sudo apt install -y caddy
sudo cp deploy/Caddyfile /etc/caddy/Caddyfile
sudo nano /etc/caddy/Caddyfile   # 改成你的域名
sudo systemctl reload caddy

Caddy 会自动申请证书、自动续期、自动做 HTTP→HTTPS 跳转。


七、不建议的做法

做法 为什么不行
51194 直接开到公网 绕过 HTTPS,密码与令牌明文暴露;限流可被伪造 IP 绕过
用 IP + 自签证书 浏览器持续警告,手机 PWA 装不了,Service Worker 无法注册
http:// 访问 同上,且离线能力失效
把数据库文件放进 web 可访问目录 等于把全部财务数据公开下载
用 root 跑服务 一旦被攻破就是整台机器沦陷
关掉登录限流 公网上几分钟就会被暴力破解
只做本地备份 服务器挂了数据一起没
用默认端口 + 默认路径 增加被扫描发现的概率(收益有限,但成本也低)
大陆服务器不做备案就上 80/443 会被云厂商封端口

八、结论与建议

推荐:香港 / 海外节点 + 域名 + Caddy。

  • 成本约 ¥120~290/年,日均不到 1 元
  • 免备案,当天可上线
  • 安全上只要把 2.1 的 6 项改掉,配合「应用只监听回环 + 防火墙只开 22/80/443 + 异地备份」,个人财务数据的风险是可控的

如果只想最快用上,可以先走「虚拟局域网」(Tailscale / 蒲公英):免费、5 分钟、完全不暴露公网,安全性最高。等确定要长期 24 小时在线,再按本文迁到云服务器——迁移成本很低(就一个 book.db 文件)。

不建议在大陆节点上线除非确实需要合规,因为备案会明显拖慢节奏,而续费价格也高得多。

posted @ 2026-09-12 12:07  omig001  阅读(6)  评论(0)    收藏  举报