--- title: "8.4 安全检查与问题排查" description: "开发前/中/后的安全清单,以及常见安全问题的症状→原因→解决方案" chapter: "第八章" --- # 8.4 安全检查与问题排查 > **本节目标**:建立安全开发的阶段意识,遇到问题时能快速定位和解决。 --- ## 安全清单 小明经历了密钥泄露、管理后台被闯入、CORS 报错之后,问老师傅:"有没有一个清单,让我每次开发时对着检查,不要再踩坑了?" 老师傅说:"有。安全不是上线前突击检查一次就完事的,它贯穿整个开发周期——从创建项目的第一秒开始。" **开发前——地基要打好。** 小明回想自己的经历:如果一开始就配好了 `.gitignore`,密钥就不会泄露;如果一开始就选了 Better Auth,就不会想着自己写认证。很多安全问题的根源是"一开始没做对",等到出了事再补救,代价比一开始就做对大得多。这个阶段要确认三件事:`.gitignore` 配置好了吗?确保 `.env`、`node_modules/`、`.next/` 都在屏蔽列表里——这是你创建项目后应该做的第一件事,甚至在写第一行业务代码之前。`.env.example` 创建了吗?列出所有需要的环境变量名,不填值,提交到 Git——这是给未来的自己和可能的协作者留的路标。认证方案选好了吗?用 Better Auth 等成熟库,不要自己写——8.2 已经解释了为什么。 > 跟 AI 说:"帮我检查项目的 .gitignore 是否完整,是否有敏感文件被 Git 追踪。" **开发中——随手检查。** 开发过程中最容易犯的错误是"先跑通再说"——密钥先写死在代码里,路由保护先不加,输入校验先跳过。"等功能做完了再回来补",但你永远不会回来补。小明就是这样一步步积累了安全债务,最后一起爆发——密钥泄露、管理后台裸奔、用户输入没校验,每一个都是"先跑通再说"留下的坑。这个阶段要养成随手检查的习惯:密钥都在 `.env` 里吗?在代码中搜索 `sk-`、`password`、`secret` 这些关键词,看有没有硬编码的密钥。Server/Client 边界清晰吗?敏感数据只在服务端处理,前端变量加 `NEXT_PUBLIC_` 前缀。路由保护到位了吗?Middleware 加 API 内部双重检查。用户输入有校验吗?后端用 Zod 校验所有输入,不信任前端传来的任何数据——前端校验是为了用户体验,后端校验才是为了安全。改了 `.env` 重启了吗?环境变量只在启动时加载,改了要重启。 > 跟 AI 说:"审查我的代码,找出所有硬编码的密钥、缺少输入校验的接口、以及没有权限检查的路由。" **上线前——最后一道关。** 小明的应用要部署了。他以为把代码推上去就行,但老师傅说:"上线前还有几件事必须检查,不然上线就是裸奔。"依赖有漏洞吗?运行 `pnpm audit`,修复高危漏洞——你的项目可能间接依赖了几百个包,每个包都可能有已知漏洞。生产环境变量配好了吗?在部署平台的环境变量页面逐一配置,注意有些值在本地和生产环境是不同的(比如 `BETTER_AUTH_URL` 本地是 `http://localhost:3000`,生产环境要改成你的域名)。HTTPS 启用了吗?Vercel、Railway 等平台默认启用,自建服务器需要配置——没有 HTTPS,用户的密码和 Cookie 在网络传输中就是明文的,任何人都能截获。错误信息安全吗?生产环境不要返回堆栈信息(stack trace),只返回友好的错误提示——堆栈信息会暴露你的代码结构、文件路径、甚至数据库表名,这些都是攻击者梦寐以求的情报。 **上线后——持续维护。** 应用上线不是终点,而是另一个起点。小明的应用跑了三个月,某天收到 GitHub 的安全告警邮件:"你的项目依赖 `lodash@4.17.20` 存在原型污染漏洞。"他很困惑——这个包他从来没直接安装过。查了一下才发现,是他安装的某个包依赖了另一个包,那个包又依赖了这个有漏洞的包。npm 生态就是这样——你的项目可能间接依赖了几百个包,每个包都可能出问题。这个阶段要做的事:密钥每 3-6 个月轮换一次(即使没有泄露,定期更换也是好习惯),每月运行 `pnpm audit` 检查依赖漏洞,关注异常登录(同一账号短时间内从不同地区登录)、频繁失败的请求(可能是暴力破解)、突增的 API 调用(可能是有人在刷你的接口)。 --- ## 问题排查手册 安全相关的报错往往让人一头雾水——错误信息不直观,原因可能在你意想不到的地方。小明踩过的每一个坑,都变成了这本手册里的一条。遇到问题?按症状查。 **`.env` 配置了但读不到。** 症状是 `process.env.XXX` 返回 `undefined`。先重启——环境变量只在启动时加载,`Ctrl+C` 停掉服务重新 `pnpm dev`,90% 的问题到这里就解决了。还不行的话,检查变量名拼写是否完全一致(注意大小写),确认 `.env` 文件在项目根目录(不是在 `src/` 里),检查有没有多余的空格或引号——`.env` 里不需要引号,`DATABASE_URL=postgresql://...` 是对的,`DATABASE_URL="postgresql://..."` 可能出问题。 **Server 能读到但 Client 读不到。** 症状是 API Route 里能读到环境变量,React 组件里是 `undefined`。原因是没加 `NEXT_PUBLIC_` 前缀——这是 Next.js 的安全机制,默认不把环境变量发送到浏览器。如果这个值确实需要在前端使用且不敏感,改名为 `NEXT_PUBLIC_XXX`。如果是敏感值(API Key),不应该在前端使用——改为在 API Route 里调用。 **跨域报错(CORS)。** 症状是浏览器控制台显示 `Access to fetch at 'xxx' has been blocked by CORS policy`。如果前后端在同一个 Next.js 项目里,不应该出现 CORS 问题——检查请求 URL 是否写错了(比如写了完整的 `http://localhost:3000/api/...` 而不是相对路径 `/api/...`)。如果调用的是外部 API,需要在后端配置 CORS,允许你的域名访问。有时候是开发环境特有的问题,检查 `next.config.js` 里的 `rewrites` 配置。 **`.gitignore` 配了但文件仍被追踪。** 症状是 `.env` 已加入 `.gitignore`,但 `git status` 仍然显示它。原因是文件在加入 `.gitignore` 之前已经被 Git 追踪过——`.gitignore` 只对"从未被追踪的文件"生效。解决方法是执行 `git rm --cached .env` 移除缓存,再提交一次。 **密钥已经泄露了。** 症状是密钥被提交到了 GitHub(公开或私有仓库)。紧急处理四步走:立即去对应平台(OpenAI、Supabase、GitHub 等)重新生成密钥;用新密钥更新 `.env`;告诉 AI "我的 .env 文件被提交到了 Git 历史中,帮我用 git filter-branch 或 BFG 工具彻底清除";检查账单是否有异常消费。 ::: danger 删除文件再提交不够 `git rm .env` 然后提交,只是在最新版本里删除了文件。Git 历史中的每一个旧版本仍然包含这个文件。任何人 `git log` 都能找到。必须清洗历史。 ::: **登录后仍被重定向到登录页。** 症状是登录成功了,但访问受保护页面还是跳转到 `/login`。先检查 Cookie 是否正常设置——打开浏览器 F12 → Application → Cookies,看有没有 session cookie。然后确认域名一致——开发环境用 `http://localhost:3000`,不要用 `http://127.0.0.1:3000`,Cookie 绑定域名,域名不同 Cookie 不共享。最后检查 Middleware 的 `matcher` 配置,确认没有把 `/login` 也保护了(登录页不应该被保护)。 你不需要记住所有这些检查项。定期让 AI 帮你审查: > "审查我的项目安全状况:检查是否有硬编码密钥、.env 是否被 Git 追踪、依赖是否有已知漏洞、所有受保护路由是否都有权限检查。" --- ::: tip 本节核心要点 - 安全清单按阶段执行:开发前配好 `.gitignore`,开发中检查边界和校验,上线前审计依赖,上线后定期轮换 - 90% 的 `.env` 问题重启就能解决 - `.gitignore` 只对未追踪的文件生效,已追踪的要先 `git rm --cached` - 密钥泄露后必须重置密钥 + 清洗 Git 历史,光删文件不够 ::: ::: info 下一步 清单和排查手册都有了,接下来去 [进阶安全防护](./05-advanced-security.md)——了解 SQL 注入、XSS、CSRF 等更深层的安全威胁,以及 AI 应用特有的安全问题。 :::