CityVistion 项目全面审核报告
总体结论:有隐患,存在 1 个 P0 生产事故级问题。
核心服务全部 active,但
核心服务全部 active,但
www.cityvistion.cn 全站静态资源 403,且存在多个安全/配置隐患需立即处理。
P0 严重问题
2
P1 高危问题
7
P2 中危问题
12
核心服务状态
全部 active
一、P0 严重问题(立即修复)
P0 1. www.cityvistion.cn 全站静态资源 403 Forbidden
| 位置 | /etc/nginx/sites-enabled/cityvistion-migration |
| 根因 | /www/wwwroot/cityvistion.cn 是符号链接指向 /root/cityvistion.cn;/root 目录权限为 drwx------,nginx worker(www-data)无法进入 |
| 影响 | 首页、控制台、管理后台、登录页、JS/CSS/图片、CesiumJS vendor 全部无法加载 |
| 修复 | 推荐将 /www/wwwroot/cityvistion.cn 改为独立实体目录(复制内容过来),或调整 Nginx location /static/ 指向并对 www-data 开放读取权限 |
P0 2. Open-Meteo 地理编码成功时抛出 NameError
| 位置 | /root/cityvistion.cn/app/public.py:558 |
| 根因 | print() 中使用了未定义变量 name 和 country_code |
| 影响 | 城市生成时 OSM 地理编码本可成功,但因异常回退到 Nominatim/Overpass,失败时返回 502 |
| 修复 | 改为 best.get("name")、best.get("country_code") |
二、P1 高危问题(今日内修复)
| # | 问题 | 位置 | 影响 | 修复建议 |
|---|---|---|---|---|
| P1 3 | /opt/cityvistion/.env 全局可读(644) |
/opt/cityvistion/.env |
任何本地用户可读取 OSS AK/SK、JWT Secret、API Key、Redis 密码 | chmod 600 并轮换其中所有密钥 |
| P1 4 | cityvistion / cgajs-api 服务以 root 运行 |
/etc/systemd/system/cityvistion.service/etc/systemd/system/cgajs-api.service |
一旦被漏洞利用,直接获得 root 权限 | 创建专用系统用户,systemd 中设置 User= / Group= |
| P1 5 | Redis 监听 0.0.0.0,且 UFW 允许旧服务器访问 |
/etc/redis/redis.conf、UFW |
Redis 密码直接暴露在网络层,存在爆破/漏洞利用风险 | Redis 改为 bind 127.0.0.1 ::1;删除旧 UFW 规则 |
| P1 6 | Runtime /api/v1/cv/jobs/{job_id} 未授权访问 |
/opt/cityvistion/app/main.py:95 |
知道 job_id 即可获取任务结果,绕过 API Key 校验 | 增加 Depends(verify_api_key) |
| P1 7 | Embedding /embed 接口未认证且对公网开放 |
/opt/cityvistion/app/embedding_service.py:38Nginx runtime 配置 |
任何人可无限制调用本地 bge-m3 模型,造成 DoS | 增加 API Key 校验,或 Nginx 层限制仅 127.0.0.1 访问 |
| P1 8 | cgajs-api CORS 仍允许旧域名 | /www/wwwroot/cgajs-api/main.py:87-108 |
旧域名若被第三方注册或原服务器被入侵,可利用 CORS 发起跨域攻击 | 移除 cgajs.cityvistion.cn、rulepackage.cityvistion.cn,仅保留 *.cityvistion.cn |
| P1 9 | cgajs-api 认证 Cookie 未设置 HttpOnly | /www/wwwroot/cgajs-api/main.py:618-633 |
XSS 可导致 cgajs_token 被盗 |
httponly=True |
三、P2 中危问题(本周内修复)
| # | 问题 | 位置 | 影响 | 修复建议 |
|---|---|---|---|---|
| 10 | log_usage 参数顺序错误 | /root/cityvistion.cn/app/public.py:63 | 使用统计与计费报表数据错误 | 改为 log_usage(..., action, db, cost=cost) |
| 11 | 订阅过期直接抛 Exception,导致 500 | /root/cityvistion.cn/app/quota.py:40 | 用户订阅过期后调用接口返回 500 | 改为返回 403 或清晰提示 |
| 12 | cv_preview / cv_export 先扣配额再执行 | /root/cityvistion.cn/app/main.py | 下游失败时配额已消耗 | 先检查可用性再扣配额;失败回滚 Redis |
| 13 | 多处硬编码弱默认密钥/密码 | /opt/cityvistion/app/config.py/root/cityvistion.cn/app/config.py | 环境变量未加载时使用弱凭据 | 默认值改为空字符串,缺失则拒绝启动 |
| 14 | 密码重置 Token 可能在前端显示 | /root/cityvistion.cn/app/main.py:464/root/cityvistion.cn/static/auth.html:139 | debug 误开启时可在页面明文看到 reset_token | 前端删除展示逻辑;后端不向前端返回 Token |
| 15 | 留言板存在存储型 XSS 风险 | /root/cityvistion.cn/app/messages.py | 可导致存储型 XSS | 后端做 HTML 转义,前端使用 textContent |
| 16 | 文件上传无大小/内容校验 | /root/cityvistion.cn/app/files.py:148-200 | 存在 DoS 与恶意文件上传风险 | 增加大小限制、MIME 白名单、文件头 magic 校验 |
| 17 | /api/v1/auth/* 限流豁免,存在爆破风险 | /root/cityvistion.cn/app/main.py:141 | 登录/注册/重置密码无 IP/账号级限流 | 对认证端点单独设置更严格限流 |
| 18 | certbot 钩子硬编码阿里云 AK/SK | /etc/letsencrypt/hooks/add_txt.py/etc/letsencrypt/hooks/del_txt.py | 泄露后可用于操作 DNS 记录 | 迁移到环境变量文件或改用 RAM 角色 |
| 19 | Nginx 缺少基础安全响应头 | /etc/nginx/sites-enabled/cityvistion-migration | 增加点击劫持、MIME 嗅探、XSS 风险 | 添加 X-Frame-Options、HSTS 等 |
| 20 | cgajs-api 备份文件世界可读 | /www/wwwroot/cgajs-api/*.bak.* | 历史版本可能含旧密钥/逻辑 | 删除或移至非 web 目录并限制权限 |
| 21 | 旧域名大量残留在前端与 CGA 文件中 | IDE、Marketplace、CGA 纹理 URL | 用户可能跳转到旧域名,资源 404 或证书错误 | 按 /root/TASKS.md P2/P3 批量清理 |
四、P3 建议项(后续优化)
运维与可观测性
- 增加统一
/health健康检查端点(www.cityvistion.cn/health、api.cityvistion.cn/health) - 为应用日志配置 logrotate / journald 容量限制
- 配置自动化数据库备份与
/www/wwwroot/配置备份 - 为 systemd 服务增加
MemoryMax、CPUQuota、WatchdogSec - fail2ban 增加 nginx 相关 jail
- PostgreSQL 调优:调高
shared_buffers、work_mem,开启慢查询日志
架构与代码
- 清理
/www/wwwroot/下旧域名目录名(marketplace.cityvistion.cn、pic.cityvistion.cn等) - 批量替换 Marketplace
.cga文件中的pic.cityvistion.cn纹理 URL - 统一 CityVistion 全站导航栏
- 处理
/static/board.html与/static/message-board.html二选一 - 清理
static/updata.html等文档中的旧域名描述 - 更新各子站点
site-version.json async函数中同步requests改为httpx.AsyncClient
五、运行状态快照
| 项目 | 状态 |
|---|---|
| 核心服务 | 全部 active |
| 磁盘 | 40G 已用 21G(55%) |
| 内存 | 7.1G,可用约 4.1G |
| SSL 证书 | cityvistion_wildcard,有效期至 2026-09-19 |
| 数据库 | cityvistion 12MB,cgajs 24MB,连接正常 |
| Nginx 配置语法 | nginx -t 通过 |
| 监控栈 | Prometheus、Grafana、Loki、Promtail、Node Exporter 均 active |
值得肯定的方面: 支付回调签名校验完整、服务间 HMAC-SHA256 鉴权合理、JWT 黑名单机制正确、PostgreSQL 未暴露公网、SSL 通配符证书已覆盖所有子域、敏感配置文件(
/etc/cityvistion/、/etc/cgajs/)权限合规。
六、原技术路线图
原 updata.html 中的 CGA.js / NL2CGA 技术路线图已归档到:
https://cgajs.cityvistion.cn/updata-roadmap.html