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.js、server/config.js |
攻击者每次请求伪造一个 IP,登录失败限流完全失效,可无限猜密码;审计日志 IP 全是假的 | 30 min |
| 2 | 去掉 URL 查询参数里的令牌,下载改为「一次性短时票据」或 fetch + Blob | server/api.js、server/auth.js、public/js/api.js |
令牌会写进浏览器历史、反向代理访问日志、Referer 头,等于把 30 天有效的钥匙到处撒 | 60 min |
| 3 | 安全响应头:X-Frame-Options: DENY、Referrer-Policy、Content-Security-Policy、HTTPS 下加 Strict-Transport-Security |
server/index.js |
可被 iframe 点击劫持;XSS 可外联;协议降级无提示 | 30 min |
| 4 | CORS 从 * 收紧:默认同源不发头,跨域需显式白名单 |
server/index.js、server/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)—— 这条在公网环境里价值最高
- CSP:
default-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 文件)。
不建议在大陆节点上线除非确实需要合规,因为备案会明显拖慢节奏,而续费价格也高得多。
浙公网安备 33010602011771号