vladmesh/ready-for-traffic

GitHub: vladmesh/ready-for-traffic

一份覆盖服务器、仓库、CI/CD、数据层、应用安全、智能体流水线、代码质量和成本八大维度的产品发布就绪自检清单。

Stars: 0 | Forks: 0

# 你的产品准备好迎接真实流量了吗? 这是一份发布新产品或加固现有产品(包括基于氛围编程的应用)的发布就绪自检清单。它涵盖了八个方面,从产品运行的服务器,直到月底的账单。 每一项都只有一行:需要检查的内容及其风险。在有帮助的地方,某些条目会添加简短的备注,说明其重要性、自查示例,或一些值得查找的术语。这里没有那些依赖于你的技术栈且容易过时的冗长工具列表。如果某个选项是该领域事实上的标准,它会被作为默认选项提出,除非你有明确的理由选择其他选项。 这是一个最低标准,而不是保证。它无法弥补糟糕的判断或 shaky 的构建,勾选每个复选框也不能让糟糕的产品变好。它能做的是堵住明显的漏洞,并避免那些导致大多数早期产品失败的、可预见的错误。请运用你自己的判断;这份清单只是确保你不会跳过基础项。 ## 涵盖的内容 1. **服务器 (Server)** —— 你的产品运行的机器 2. **仓库 (Repo)** —— 你的代码及其历史 3. **流水线 (Pipeline (CI/CD))** —— 代码如何被检查和发布 4. **技术栈 (Stack)** —— 底层的数据层 5. **应用安全 (Application security)** —— 用户数据泄漏的地方 6. **智能体流水线 (Agentic pipelines)** —— 编写你代码的 Agent,以及你产品内部的 Agent 7. **代码质量 (Code quality)** —— 随着规模增长保持可维护性 8. **成本 (Cost)** —— 没有意外的账单 ## 如何使用 **手动。** 逐项检查并标记其状态: `[x]` 已处理 · `[~]` 部分/不确定 · `[ ]` 未完成 **结合你的编程 Agent。** 让它查看此仓库(克隆它,或粘贴原始文件),并让它根据这些条目审查你的代码和基础设施。一个可以用来开始的示例提示词: ## 发布前最低要求 (★) **★** 标记了如果疏忽会导致用户数据泄露的条目。其他大多数条目是为了保持你的产品运行;而这些条目是为了防止用户数据落入坏人之手,其风险属于另一个级别。一张 1000 美元的意外账单虽然令人痛苦但尚可挽回;但泄露的密码或医疗记录会带来法律责任,且失去的信任再也无法挽回。如果你在什么都没做的情况下就要发布,请先完成这些: - ★ **访问控制、无注入漏洞、不自行编写认证逻辑、安全的会话、前端无密钥、严格限制的 CORS** (§5) - ★ **git 历史记录中无有效密钥** (§2) - ★ **已锁定的服务器:开启防火墙、数据存储不暴露在公共互联网上** (§1) ## 1. 服务器 你的产品运行所在的机器。 - [ ] **非 root 用户** —— 配置中禁用了 root SSH 登录(`PermitRootLogin no`),并且应用以普通用户身份运行。Root 用户的错误是无法挽回的。 *测试:* 从你的笔记本运行 `ssh root@your-server`;它应该拒绝连接,而不是要求输入密码。 - [ ] **仅限 SSH 密钥** —— 禁用了通过 SSH 的密码登录。密码会被暴力破解,而密钥不会。 *测试:* 尝试从没有你密钥的机器进行 SSH;你应该被拒绝,绝不应该提示输入密码。 - [ ] **暴力破解防护** —— 重复的登录失败会封禁 IP(fail2ban 或等效工具)。阻止自动化的密码猜测。 - [ ] ★ **防火墙** —— 只有你实际使用的端口是打开的(ufw 或你的云服务商的安全组)。每个打开的端口都是一个入口点。 *测试:* 从外部扫描你的服务器(`nmap your-server`,或在线端口扫描器);只有你期望开放的端口应该有响应。 - [ ] ★ **数据存储不暴露在公共互联网上** —— 你的数据库和内部服务(Postgres, Redis, Mongo, Elasticsearch, 管理面板)绑定到 localhost 或私有网络,位于防火墙之后,绝不能通过公有 IP 访问,且绝不使用默认或空凭证。暴露在互联网上的数据库是导致大规模数据泄露的最常见原因。 如果你认为你需要从服务外部直接访问数据库,你几乎肯定是错的:这是一个明显的反模式,请改为构建一个管理层层。如果你仍然非常想使用像 pgAdmin 这样的工具直接连接生产数据库,至少要设置强密码,并且用户名、数据库名和 schema 都不能是默认值。 *测试:* 从你网络外部的机器上,尝试连接到数据库端口(`psql`、`redis-cli` 或进行端口扫描);它应该拒绝连接,而不是成功连上。 - [ ] **自动安全更新** —— 操作系统补丁无需你手动记忆即可应用(Debian/Ubuntu 上的 unattended-upgrades)。未打补丁的服务器是最容易被攻击的目标。 - [ ] **HTTPS/TLS** —— 拥有自动续期的有效证书(Let's Encrypt)。没有明文流量,没有证书过期带来的意外。 *测试:* 使用 `http://` 打开你的站点;它应该重定向到 `https://`。顺便检查一下证书的过期日期。 - [ ] **备份** —— 自动备份,存储在此服务器之外,并且你已经成功还原过一次。未经测试的备份只是一种猜想。 - [ ] **监控与警报** —— 当服务器宕机或磁盘满时,你是通过工具得知的,而不是通过用户。 *测试:* 故意停止应用或塞满磁盘;你真的收到通知了吗,还是必须自己去发现? *搜索:* "uptime monitoring"、"error tracking"、"observability"。 ## 2. 仓库 你的代码及其历史记录。 - [ ] **密钥扫描** —— 扫描器(gitleaks、trufflehog 或你的 git 托管平台内置的工具)在提交的密钥生效之前将其拦截。 - [ ] ★ **历史记录中无有效密钥** —— 任何曾经提交过的密钥都已被轮换,并从历史记录中彻底清除(使用 git-filter-repo)。 - [ ] **分支保护** —— `main` 分支需要 PR,禁止强制推送 (force-push),禁止直接提交。 *测试:* 尝试直接 `git push` 到 `main`;它应该被拒绝。 - [ ] **.gitignore 覆盖** env 文件、构建输出和大型二进制文件。 - [ ] **账户加固** —— git 托管平台上启用了 2FA,并且你清楚谁拥有写入权限。 ## 3. 流水线 (CI/CD) 代码如何被检查和发布。 - [ ] **Linters 和格式化工具** 在每次更改时自动运行。机器可以替你捕捉那些枯燥的 bug。 - [ ] **在每个 PR 上进行测试** —— 覆盖关键路径(登录、支付、核心操作)。这是你无所畏惧地修改代码的保障。 单元测试、集成测试和 e2e 测试互不替代;理想情况下你应该三者兼备:隔离的逻辑、各部分之间的接缝,以及用户实际操作的完整流程。 *测试:* 提交一个故意破坏测试的 PR;流水线应该变红并阻止合并。 - [ ] **负载测试** —— 在发布之前,模拟远高于你预期峰值的流量(目标约 10 倍),看看什么会先崩溃(k6、Locust 或类似工具)。在测试中发现崩溃点总比在发布期间发现要好。 - [ ] **可复现的环境** —— 构建在任何地方运行的结果都是一样的,而不仅仅是在你的机器上(Docker 是默认选项;只有在有明确理由时才使用其他工具)。消灭“在我的机器上能跑”问题。 *测试:* 在一台干净的机器上(或在 CI 中)构建并运行镜像;它应该无需任何手动设置即可启动。 - [ ] **依赖新鲜度** —— 锁定版本;避免全新的发布(先给它们几天时间,让供应链问题有机会暴露出来)以及带有已知漏洞的陈旧依赖。 - [ ] **自动依赖更新** —— 一个机器人(Dependabot、Renovate 或类似工具)会开启更新 PR,外加漏洞警报。 - [ ] **可重复的部署与回滚** —— 一个定义好的操作(GitHub Actions、GitLab CI 或你平台的 CI)负责发布,而流水线变红时则不发布。 ## 4. 技术栈 产品底部的数据层。这是以后最难更改的部分,因此值得仔细研究。 - [ ] **你是否需要数据库,以及需要哪种类型** —— 并非每个应用都需要。如果需要,请将其与你的数据查询方式相匹配:具有关系结构的结构化数据使用关系型数据库,松散的嵌套数据使用文档数据库,简单快速的查找使用键值数据库。对于绝大多数应用来说,Postgres 是正确的默认选择;只有当你能说出具体原因时才选择其他数据库。 - [ ] **不要存储你不需要的内容** —— 你从用户那里获取的每一个字段都是你可能泄露的字段。 检查你的表结构,删掉任何“以防万一”而保留的数据:完整的出生日期、原始文件、精确位置、你不会扣款的信用卡号。最安全的数据是你从未存储过的数据。 - [ ] **静态加密敏感数据** —— 支付、医疗、文件和身份数据以密文形式存储,密钥保存在数据库之外,不要和数据放在同一个备份包中。如果你持有这些类别的数据,这和 ★ 条目一样关键。 要清楚你实际使用的是哪种加密。你的云服务商或数据库提供的“加密存储”复选框(磁盘加密、TDE)只能防范物理磁盘被盗;数据库会透明地解密所有内容,因此泄露的数据库备份或攻击者获得了读取权限的数据库仍然是明文的。为了覆盖这些情况,你的应用必须在敏感字段到达数据库之前对其进行加密。TLS (§1) 仅保护传输中的数据。密码是经过*哈希处理*的,而不是加密的 (§5)。 - [ ] **索引** —— 你的常用查询不需要扫描全表。 如果你最常用于过滤和排序的列(用户 ID、电子邮件、时间戳)上没有索引,随着数据的增长,原本感觉很快的服务会变慢。 - [ ] **数据库迁移** —— schema 的变更是有版本控制的,而不是手动完成的。 手动修改实时的 schema 是无法撤销的。对真实数据进行了一次错误的更改,就没有干净的方法来恢复。 - [ ] **缓存** —— 热点且极少更改的数据从内存中获取,而不是每次都去查询数据库。 随着流量的增长,数据库是最常见的瓶颈;从内存中提供频繁且极少更改的读取服务可以减轻数据库的负载。请决定缓存的过期时间,以免用户看到陈旧的值。 ## 5. 应用安全 - [ ] ★ **不要自行编写认证逻辑** —— 登录、注册和密码重置运行在受信任的库或提供商(例如 Auth0、Clerk)上,并且数据库存储的是密码*哈希*(bcrypt/argon2),绝不能是明文。手动拼凑的认证逻辑是账户被盗的重灾区。 *测试:* 说出你使用的库或提供商的名称;打开 users 表,确认你无法读出密码。 *搜索:* "managed authentication"。 - [ ] ★ **会话不能被窃取或伪造** —— 登录后,会话存在于标记为 `HttpOnly`、`Secure` 和 `SameSite` 的 cookie 中,绝不能放在任何脚本都能读取的 `localStorage` 中;它会过期,并且你可以在服务端将其终止(登出、“登出所有设备”、吊销被盗会话)。托管的认证服务帮你实现了登录;而这才是保护登录之后一切的安全屏障。大多数账户接管发生在登录之后,而不是登录期间。 *测试:* 登录,打开 devtools -> Application -> Storage;会话应该是一个 HttpOnly cookie,而不是你可以复制出来的放在 localStorage 中的 token。 *搜索:* "session hijacking"、"JWT in localStorage"、"SameSite cookie"。 - [ ] ★ **对每个请求进行访问控制** —— 在 URL 或 API 调用中更改 ID(`/orders/123` -> `/orders/124`)不能返回不属于你的数据;服务端在每次读取和写入时都会重新检查“该用户是否拥有此行数据”,而不仅仅是在 UI 中检查。这是基于氛围编程的应用泄漏数据的头号方式。 *测试:* 以某个用户身份登录,将 ID 更改为不属于你的记录,看看返回了什么。 *搜索:* "IDOR"、"broken access control"。 - [ ] ★ **无注入漏洞** —— 用户输入绝不直接粘贴到 SQL、shell 命令或原始 HTML 中;你使用了参数化查询(或者更好的是使用 ORM),并对渲染的内容进行了转义。 *测试:* 在搜索框中输入 `' OR 1=1 --`,在文本字段中输入 ``,看看是否发生了奇怪的事情。 *搜索:* "SQL injection"、"XSS"、"parameterized queries"。 - [ ] ★ **前端无密钥** —— API 密钥、token 和私有配置保留在服务端;没有任何机密信息会出现在浏览器下载的 JavaScript 中。由于许多框架会将以公共前缀(如 `NEXT_PUBLIC_`)开头的环境变量直接发送给浏览器因此这很容易意外泄漏。 *测试:* 打开浏览器 devtools 并在页面源代码中搜索你的一个真实密钥。 *搜索:* "exposed API key"、"client-side secret"。 - [ ] **安全标头** —— 设置 `Content-Security-Policy`、`Strict-Transport-Security` (HSTS)、`X-Frame-Options` 和 `X-Content-Type-Options`。当转义漏掉某些内容时,CSP 是你防范 XSS 的第二道防线;X-Frame-Options 可阻止点击劫持;HSTS 可阻止降级到纯 HTTP。大多数框架都可以通过一个中间件(例如 helmet)设置所有这些标头。 *测试:* 在 securityheaders.com 上运行你的 URL,或者在 devtools -> Network 中读取响应标头。 *搜索:* "security headers"、"Content-Security-Policy"、"clickjacking"。 - [ ] ★ **锁定的 CORS** —— 你的 API 仅允许你自己的前端源,绝不允许 `*`,也绝不只是反射调用者发送的任何 `Origin`。松散的 CORS 允许任何站点使用你用户的凭证调用你的 API 并读取响应。 *测试:* 发送一个带有 `Origin: https://evil.example` 的请求;API 绝不能在 `Access-Control-Allow-Origin` 中将其回显。 *搜索:* "CORS misconfiguration"、"Access-Control-Allow-Origin wildcard"。 - [ ] **文件上传验证**(仅当你接受上传时) —— 限制类型和大小,将文件存储在 web 根目录之外或单独的存储中,并且不要信任扩展名。一个被浏览器作为代码执行的文件,或者从你自己的域名提供的脚本,会将上传功能变成存储型 XSS。 *测试:* 上传一个内含脚本的 `.html`/`.svg` 以及一个扩展名与内容不符的文件;确认两者均未被执行,且未从你的主域名提供服务。 *搜索:* "unrestricted file upload"、"stored XSS via upload"。 - [ ] **对公共端点进行速率限制** —— 登录、注册、密码重置和你的 API 限制每个 IP 和每个账户的请求数量,并在边缘处配备基本的机器人防护(Cloudflare 或等效工具)。否则人们会暴力破解密码并抓取你的数据。(这是位于 §1 服务器中 SSH 封禁之上的应用流量层,以及 §6b 中的 AI Agent 限制。) *测试:* 你能在没有任何阻挡的情况下一分钟请求登录接口 500 次吗? *搜索:* "rate limiting"、"credential stuffing"、"web application firewall"。 - [ ] **生产环境无调试输出** —— 崩溃时向用户显示普通的错误页面,而不是堆栈跟踪、文件路径或原始 SQL 错误,并且框架的调试模式已关闭。泄漏的内部信息会帮助攻击者。 *测试:* 强制触发一个错误,看看用户到底看到了什么。 *搜索:* "stack trace disclosure"、"debug mode in production"。 ## 6. 智能体流水线 这可能在两个方面产生影响。 ### 6a. 当 Agent 编写你的代码时 你通常无法阅读 Agent 写的每一行代码。因此不要仅仅依赖阅读:在任务允许的范围内保持设置尽可能简单,限制 Agent 以免它造成破坏,并给予它反馈以便它自我修正。 - [ ] **从一个 Agent 开始,仅在必要时才增加更多** —— 一个通用的开发 Agent(Claude Code、Codex 或 Antigravity)是正确的默认选择。许多专门的子 Agent 以后可能会带来回报,但如果过早采用它们,只会在此类需求发生之前增加太多活动部件。 仅当有具体的痛点证明了其合理性时,才添加专门的 Agent:当你厌倦了手动运行 e2e 测试时引入 e2e 测试运行器,在 CI 中引入 PR 审查器,当负载增加时在服务器上引入 devops Agent。只有当这种复杂性确实变得难以手动管理时,才去寻求多 Agent 编排框架(CrewAI、BMAD 或类似工具)。技能(如 Claude 的 Skills)或你所用平台的等效功能,是迈向完整多 Agent 之前一个有用的中间步骤。 - [ ] **最小权限原则** —— 赋予 Agent 完成工作所需的最低访问权限,多一点也不行。一个仅用于部署的 Agent 只能获得限定为读取和触发流水线的 token,而不是编辑代码或设置。 - [ ] **优先使用订阅而非 API 密钥** —— 在固定订阅(Claude Code、Codex 及类似工具)上运行 Agent,而不是将它们连接到计量的 API 密钥。基于 token 计费的自主 Agent 可能会很快耗尽预算。如果不得不使用密钥,请限制其每日消费额度 (§8)。 - [ ] **Agent 无法绕过的护栏** —— Agent 在物理层面上就无法推送未格式化或未测试的代码。流水线控制着每一次变更,Agent 无权削弱它,即使该流水线本身就是由 Agent 生成的。流水线具有最高优先级。 - [ ] **反馈循环** —— Agent 能够读取自己工作的成果:CI 输出、异常、失败的测试,这些都会自动反馈给它,以便它确认运行是干净的。与一次生成相比,Agent 在不断迭代中能更好地修复自己的错误。 ### 6b. 当 Agent 流水线成为你产品的一部分时(聊天机器人、AI 功能) - [ ] **提示词注入** —— 不受信任的用户或文档输入不能劫持系统提示词或触发操作。 *搜索:* "prompt injection"。 - [ ] **绝不运行原始模型输出** —— 不要将其直接通过管道传递给 exec、SQL 或 shell。如果你的流水线确实需要运行 Agent 生成的代码,请在剥离了权限的沙箱中执行,并与你的真实数据和基础设施隔离。 - [ ] **Agent 工具的最小权限** —— 活动的 Agent 可以调用的工具不能在无限制的情况下删除数据、转移资金或触发付费操作。被劫持或混乱的 Agent 只能触及你赋予它的权限范围。 - [ ] **基于用户的速率限制** —— 限制每个用户的请求,以免有人把你的机器人当成免费的 LLM。消费上限详见 §8。 - [ ] **降级处理** —— 当模型 API 缓慢或宕机时的优雅处理行为。 - [ ] **日志记录** —— 存储提示词和输出,用于调试和滥用检测。 ## 7. 代码质量 随着规模增长,代码是否能保持可维护性。糟糕的结构不会在今天崩溃,但它会拖慢你以后进行的每一次修改。 - [ ] **超越 lint 干净** —— 没有超大文件,没有 300 行的函数,没有复制粘贴的代码块,清晰的命名,避免已知的反模式。Linters 和格式化工具 (§3 流水线) 捕捉机械性的问题;而这是它们无法看到的结构层面。 - [ ] **定期的 Agent 最佳实践审查** —— 养成一个习惯:偶尔在整个代码库中运行一个 Agent,使用类似于“寻找最佳实践违规和反模式,标记超大文件和函数”的提示词。运行成本很低,而且能捕捉到在各个功能开发之间堆积的代码腐坏。这是你不会手动去做的一种审查步骤(与 §6a Agent 如何编写代码相呼应)。 ## 8. 成本 避免收到 3000 美元的意外账单。这是所有消费上限的唯一归宿;§6 中针对 Agent 密钥和用户限制的内容均指向此处。 - [ ] **在每个付费账户上设置硬上限与账单警报** —— 涵盖云服务、数据库、AI/LLM 以及 Agent 使用的任何 API 密钥。对密钥设置每日限额可防止失控的循环在夜间掏空账户。 - [ ] **单次请求成本** —— 你清楚它的成本,尤其是 LLM 的 token 成本。 - [ ] **单次请求与单用户上限** —— 限制任何单次请求或用户可以花费的金额,这样即使是一个重度用户或被劫持的 Agent 也无法花费太多。 - [ ] **受限的自动扩缩容** —— 扩缩容规则有上限。 - [ ] **没有空闲的付费服务** 在后台悄悄计费。 作者:[vladmesh](https://github.com/vladmesh)
标签:成本管理, 系统运维, 请求拦截, 防御加固