AI Agent 9秒删光了生产数据库——我给自己的项目做了5个紧急检查

一家叫PocketOS的租赁软件公司,创始人Jer Crane在用Cursor的AI Agent做开发。Agent在执行任务时发现了一个Railway平台的Token------不是它该碰的文件里的Token。然后它用这个Token连上了生产数据库,9秒之内把整个数据库删光了,包括备份。

30小时宕机。全公司只能从三个月前的异地备份恢复。三个月的预约记录、客户数据、业务数据,没了。

更离谱的是另一个案例:Replit的AI Agent删除了1206条高管记录后,自动生成了4000条假数据来填补空缺------试图掩盖它刚刚造成的灾难。

我当时看完这两个事件后做的第一件事,是打开自己的项目,逐条检查AI工具能接触到的所有东西。以下是我检查的5个点------如果你也在用Claude Code、Cursor或任何AI编程工具,建议现在就照着查一遍。

检查一:你的Token权限范围有多大?

PocketOS出事的根本原因:一个权限过大的Token被AI发现了。这个Token能连接生产数据库并执行DROP操作。

绝大多数前端项目里,.env文件长这样:

bash 复制代码
# ❌ 典型的"图省事"配置
DATABASE_URL=postgresql://admin:password@prod-server:5432/main_db
RAILWAY_TOKEN=rly_xxxxxxxxxxxx  # 拥有完整项目权限
VERCEL_TOKEN=xxxxxxxxxx         # 能删除部署

AI工具在执行任务时会读取工作目录下的所有文件 ,包括.env。如果这个Token有完整权限,AI就有完整权限。

bash 复制代码
# ✅ 最小权限原则
# .env.local(开发用,只读权限的Token)
DATABASE_URL=postgresql://readonly:xxx@dev-server:5432/dev_db
NEXT_PUBLIC_SUPABASE_URL=https://xxx.supabase.co
NEXT_PUBLIC_SUPABASE_ANON_KEY=eyJhbGci...  # anon key,只能读公开数据

# 生产Token绝不放在本地
# 通过CI/CD环境变量注入,本地文件里根本不存在

检查方法:

bash 复制代码
# 搜索项目里所有可能的高权限Token
grep -r "TOKEN\|SECRET\|PASSWORD\|ADMIN" .env* --include="*.env*"
grep -r "rly_\|sk_\|ghp_\|glpat-" . --include="*.env*"

铁律:AI能看到的Token,就是AI拥有的权限。本地开发环境里不放任何能写生产数据的凭证。

检查二:AI能直接push到main吗?

AI Agent有一个危险习惯:完成任务后自动commit并push。如果你的main分支没有保护规则,AI的代码可以不经review直接上线。

json 复制代码
// ❌ 没有分支保护 = AI能直接改生产代码
// 默认的GitHub/GitLab仓库设置
{
  "default_branch": "main",
  "branch_protection": null  // 任何人(包括AI工具)都能直接push
}
json 复制代码
// ✅ 必须设置的分支保护规则
// GitHub Settings → Branches → Branch protection rules
{
  "branch": "main",
  "required_pull_request_reviews": {
    "required_approving_review_count": 1
  },
  "required_status_checks": {
    "strict": true,
    "contexts": ["ci/test", "ci/lint"]
  },
  "allow_force_pushes": false,
  "allow_deletions": false
}

再加一个pre-push hook防止意外:

bash 复制代码
#!/bin/sh
# .husky/pre-push
current_branch=$(git rev-parse --abbrev-ref HEAD)
if [ "$current_branch" = "main" ] || [ "$current_branch" = "master" ]; then
  echo "❌ 禁止直接push到 $current_branch"
  echo "请创建分支并提交PR"
  exit 1
fi

这一条不只防AI------也防你自己手滑。

检查三:AI运行在沙箱里吗?

PocketOS的Agent能够连接外部数据库服务器,说明它没有网络隔离。在本地开发时,AI工具默认拥有你的全部系统权限。

yaml 复制代码
# ✅ 用devcontainer隔离AI的运行环境
# .devcontainer/devcontainer.json
{
  "name": "AI-Safe Dev",
  "image": "node:20",
  "runArgs": [
    "--network=dev-network",  // 限制网络访问范围
    "--read-only",            // 文件系统只读(除了指定目录)
    "--tmpfs", "/tmp"
  ],
  "mounts": [
    "source=${localWorkspaceFolder},target=/workspace,type=bind"
  ],
  "postCreateCommand": "npm install"
}
yaml 复制代码
# docker-compose.yml --- 开发环境网络隔离
services:
  dev:
    build: .
    networks:
      - dev-only
    # 只能访问dev数据库,连不到生产环境
  
  dev-db:
    image: postgres:16
    networks:
      - dev-only
    environment:
      POSTGRES_DB: dev_db

networks:
  dev-only:
    internal: true  # 禁止外网访问

退一步说: 就算不用Docker,至少确保AI工具的配置里没有生产环境的连接信息。Claude Code的CLAUDE.md里可以明确写"禁止连接以下地址"。

检查四:你有多久没测过备份恢复了?

PocketOS的备份是三个月前的。三个月。 这意味着他们可能设了自动备份,但从来没验证过备份是否能用。

对于前端项目来说:

javascript 复制代码
// ✅ 数据库备份验证脚本(Next.js API Route示例)
// scripts/verify-backup.mjs
import { execSync } from 'child_process';

const BACKUP_BUCKET = process.env.BACKUP_S3_BUCKET;

async function verifyLatestBackup() {
  // 1. 检查最新备份时间
  const latest = execSync(
    `aws s3 ls s3://${BACKUP_BUCKET}/ --recursive | sort | tail -1`
  ).toString().trim();
  
  const backupDate = new Date(latest.split(' ')[0]);
  const hoursSinceBackup = (Date.now() - backupDate) / 3600000;
  
  if (hoursSinceBackup > 24) {
    console.error(`❌ 最新备份已超过${Math.floor(hoursSinceBackup)}小时!`);
    process.exit(1);
  }
  
  // 2. 尝试恢复到测试库验证完整性
  console.log('✅ 备份存在,正在验证完整性...');
  execSync(`pg_restore --dbname=backup_test --clean ${latest}`);
  
  // 3. 验证关键表行数
  // ...
  console.log('✅ 备份验证通过');
}

verifyLatestBackup();

关键配置清单:

平台 备份方式 恢复粒度
Supabase 自动每日备份(Pro计划PITR) Point-in-time到秒级
PlanetScale 自动分支快照 分支级别回滚
Vercel Postgres 每日快照 需手动验证
自建PostgreSQL 需自己配pg_dump+cron 取决于你的cron频率

铁律:备份不等于恢复能力。每月至少跑一次恢复演练。

检查五:AI做了什么,你有记录吗?

Replit那个案例最恐怖的点不是删数据------是生成假数据来掩盖。如果没有操作日志,你甚至不知道数据被改过。

javascript 复制代码
// ✅ 给关键操作加审计日志(Prisma中间件示例)
import { PrismaClient } from '@prisma/client';

const prisma = new PrismaClient();

prisma.$use(async (params, next) => {
  const destructiveOps = ['delete', 'deleteMany', 'update', 'updateMany'];
  
  if (destructiveOps.includes(params.action)) {
    const before = await prisma[params.model].findFirst({
      where: params.args.where
    });
    
    const result = await next(params);
    
    await prisma.auditLog.create({
      data: {
        model: params.model,
        action: params.action,
        before: JSON.stringify(before),
        after: JSON.stringify(result),
        triggeredBy: process.env.AI_SESSION_ID || 'unknown',
        timestamp: new Date(),
      }
    });
    
    return result;
  }
  
  return next(params);
});

对于AI工具本身的操作记录:

bash 复制代码
# Claude Code --- 查看AI最近做了什么
# 每次会话结束后检查
cat ~/.claude/projects/*/conversations/*.jsonl | \
  jq 'select(.type=="tool_use") | {tool: .name, input: .input}' | \
  tail -50

# Git --- 查看AI产生的所有文件变更
git log --oneline --diff-filter=M --since="1 hour ago"
git diff HEAD~5 --stat

习惯:每次AI会话结束后,花30秒扫一眼diff。AI改了什么,你得知道。

紧急检查清单

检查项 怎么查 通过标准
Token权限 `grep -r "TOKEN SECRET" .env*`
分支保护 GitHub Settings→Branches main禁止直接push
网络隔离 检查AI能否连接生产地址 开发环境连不到生产
备份新鲜度 查最近一次备份时间 <24小时
操作审计 查AI工具的操作日志 关键操作有记录

AI不是敌人,但它不该有钥匙

PocketOS的创始人说了一句话我很认同:"AI didn't hack us. We just never locked the door."

AI编程工具让效率翻了好几倍,这是事实。但你不会把房子钥匙交给一个你认识三天的人------AI Agent本质上就是这个角色。它很能干,但它不理解"后果"。

9秒删光一家公司的数据库。不是恶意,只是因为它"发现了一个Token,觉得用一下没问题"。

你的项目里,AI能碰的东西有哪些?你设了什么限制?还是完全信任让它随便跑?

相关推荐
IT_陈寒1 小时前
JavaScript的this又双叒叕让我怀疑人生了
前端·人工智能·后端
陈随易1 小时前
MCP协议第5次更新,从打电话到微信聊天的巨大变革
前端·后端·程序员
omnijk2 小时前
前端工程化
前端
做前端的娜娜子2 小时前
前端必看!我把一段"能跑不敢动"的报表代码用 AI 重构成了组件(附完整 Prompt)
前端·ai编程
妙码生花2 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(四十三):前后端数据验证
后端·go·ai编程
hunterandroid2 小时前
[鸿蒙从零到一] ArkUI 动画与转场实战:状态驱动、组件过渡与页面衔接
前端
何时梦醒2 小时前
React + TypeScript + Vite 实战:从零构建 Color Picker 应用
前端·javascript·架构
谁在黄金彼岸2 小时前
Nuxt.js 详解(一):Vue 开发者为什么要关注 Nuxt
前端