---
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 应用特有的安全问题。
:::
