2174 字
11 分钟
Gitea 遭遇 Spam 批量注册与恶意建仓:我的一次应急封禁与清理复盘

本文基于自建私有 Gitea 场景整理,不同版本、不同部署方式(Docker / systemd / k8s)菜单位置和命令细节可能略有差异。

文中涉及账号删除与数据库查询,请务必先备份数据库和关键配置后再执行。

如果你不清楚某条命令会造成什么影响,请先在测试环境验证,再操作生产环境。

今天遇到一个有点恶心的问题:团队私有 Gitea 遭遇自动化恶意注册,短时间出现大量异常账号,并伴随批量建仓行为,管理后台直接被刷屏。

先说结论:这次能比较快处理掉,关键不是「删得快」,而是顺序对——先止血,再筛选,最后批量清理

事件现象#

我这边最先看到的是两个信号:

  • 用户列表里突然多了很多陌生账号
  • 仓库列表出现了批量新建的可疑仓库

这时候第一反应不要急着逐个点删除。因为你删一批,对面还能继续注册一批。而且我发现的时候攻击者已经注册了上千个账号,只会越删越多。 先把入口关掉,才有资格谈清理。

应急目标#

这次处置目标我定得很明确:

  1. 立刻止血:阻断新的垃圾账号进入系统
  2. 精准识别:尽量区分团队真实账号和异常账号
  3. 批量清理:把 Spam 账号快速、安全地删掉
  4. 降低复发:补上最基本的防护和告警

Part 1:先止血(关闭自助注册)#

我第一步先去 Gitea 管理配置里禁用了用户自行注册(Disable Registration)。 这一步完成后,攻击面会立刻收缩,系统不再持续「进脏数据」。

你可以把这一步理解为:先拧紧水龙头,再擦地板。 不然地板永远擦不干净。

打开 Gitea 的配置文件 app.ini(通常位于 /etc/gitea/app.ini 或 Docker 映射的 data/gitea/conf/app.ini),找到 [service] 区块

如果你只是个人使用或团队内部使用,建议直接关闭外部注册,后续由管理员在后台手动创建账号:

[service]
DISABLE_REGISTRATION = true

如果必须允许外部注册,请开启以下选项来拦截自动化脚本:

[service]
; 要求邮箱验证 (需要配置好 SMTP 邮件服务)
REGISTER_EMAIL_CONFIRM = true
; 开启验证码
ENABLE_CAPTCHA = true
; 验证码类型:可选 image, recaptcha, hcaptcha, mcaptcha, cfturnstile 等
CAPTCHA_TYPE = image
; 开启人工审核,新注册账号必须由管理员在后台激活才可用
REGISTER_MANUAL_CONFIRM = true

修改完毕后,请重启 Gitea 服务以使配置生效。可以通过管理后台的 站点管理 -> 配置 直接查看是否已正确加载。

建议顺手检查:

  • 是否真的「保存并生效」
  • 前台注册页是否已不可用
  • 是否还有第三方入口能绕过注册策略

Part 2:数据库筛选异常账号(基于邮箱域名白名单)#

我们团队成员邮箱相对固定,主要是:

  • qq.com
  • foxmail.com
  • 163.com

所以我用了一个非常实用的思路:
先保留白名单域名账号,再把剩余账号作为「待复核对象」。

WARNING

注意:白名单策略只能提高效率,不能替代人工复核。

某些机器人账号、历史测试账号、外协账号可能不在白名单里,直接删除有误伤风险。

在导出后请务必检查所有正常用户的 id 是否被排除,我在这一步就险些删掉了我们用 163 邮箱和 foxmail 邮箱注册的两个正常账号。

下面给一个参考的 SQL 思路(这里我是 sqlite,请按你的数据库类型调整):

Terminal window
# 如果是 Docker 部署,请进入容器内执行该命令,或者根据宿主机的 volume 映射路径进行操作。
# 首先备份数据库防止误删
cp /var/lib/gitea/data/gitea.db /var/lib/gitea/data/gitea.db.bak
# 排除所有已知团队邮箱后缀,筛选疑似异常账号,并写入到 ids.txt 中
sqlite3 /var/lib/gitea/data/gitea.db "SELECT id FROM user WHERE email NOT LIKE '%@qq.com' AND email NOT LIKE '%@foxmail.com' AND email NOT LIKE '%@163.com' AND is_admin = 0 AND type = 0 AND id > 0;" > ids.txt

我这里就是先查、先导出、先复核,没有在数据库里直接 DELETE。 原因很简单:账号删除最好走 Gitea 自己的管理逻辑或 CLI,减少关联数据残留和不可预期问题。

Part 3:用 Gitea CLI 批量删除 Spam 账号#

筛选出待处理账号后,我用 Gitea CLI 做批量删号。 这样比手点后台快很多,也更可重复执行。

Terminal window
# 逐行读取 ids.txt 并执行删除操作
for id in $(cat ids.txt); do
echo ">>> 正在处理用户 ID: $id"
# 使用 --purge 参数强制连带仓库一起删除
docker exec -u git gitea gitea admin user delete --id "$id" --purge
done
echo "清理完成!"

如果你没有使用 docker 而是直接使用宿主机直接部署:

Terminal window
# 在执行下面的循环删除前,强烈建议先 `vi ids.txt` 看一眼,确保里面没有你眼熟的正常同事的 ID,要不然就真的要「运维祭天」了
for id in $(cat ids.txt); do
echo ">>> 正在处理用户 ID: $id"
# sudo -u git 表示以 git 用户的权限运行 gitea 命令
# 如果提示找不到配置文件,需要在命令后追加 --config /etc/gitea/app.ini (替换为实际路径)
sudo -u git gitea admin user delete --id "$id" --purge
done
echo "清理完成!"

然后就可以切到管理面板看烟花了(笑)

清理后我做了哪些验证#

批量删除跑完不代表结束,我又做了几步确认:

  1. 用户总量是否回到合理区间
  2. 可疑仓库是否已经同步清理(必要时再做仓库侧处理)
  3. 团队成员账号登录是否正常
  4. 管理后台无持续新增异常账号
  5. 日志中没有新的批量注册行为

只有这几项都正常,我才把这次应急判定为「处理完成」。

环境差异:为什么另一实例暂时没中招#

我这次也检查了另一套 Gitea。那套虽然当时允许注册,但因为放在域名反代后,且只允许本地网络访问(没有公网直接入口),目前没有出现类似的 Spam 注册和批量建仓问题。

这件事给我的提醒是:

  • 是否开放注册,决定「能不能注册」
  • 是否暴露公网入口,决定「谁能来注册」
  • 反向代理和访问控制策略,决定「攻击面有多大」

也就是说,开放注册本身不一定马上出事;但「开放注册 + 公网可达 + 缺少限流/校验」这个组合,风险会明显上升。
虽然另一实例暂时没遇到问题,我还是把它的注册入口也关了。与其等流量打过来再救火,不如先把默认攻击面降下来。

这次踩坑里最容易忽略的点#

1) 不要上来就数据库硬删#

直接在数据库 DELETE 很爽,但容易留下关联问题,比如不能清理干净仓库、issue 等数据,容易残留。 账号体系尽量通过 Gitea 管理面或 CLI 做删除,让应用层处理关系数据。

2) 白名单不是绝对安全#

域名只是快速过滤条件,不是身份认证。 能人工复核的尽量复核,尤其是管理员、机器人账号和历史服务账号。

3) 先止血比「删得快」更重要#

攻击还在持续时,清理动作的边际收益很低。 先封入口,后清场,这个顺序别反。

事后加固(防止再来一轮)#

为了防止攻击者复活的风险,我们可以:

  • 保持关闭开放注册,改为邀请制/管理员审批
  • 开启并强化验证码、人机校验、邮件验证
  • 检查管理员账户口令强度与 2FA
  • 检查是否暴露了不必要的公开入口
  • 尽量通过反向代理、不使用默认的 3000 端口作为入口
  • 定期检查账号情况,遇到异常情况及时清理

总结#

这次事件本质上不复杂,但很考验应急顺序。 我的经验可以浓缩成一句话:

先止血、再筛选、后清理,最后补防线。

如果你现在也在维护私有 Gitea,我建议你今天就做两件事:

  1. 检查是否还开着公开注册
  2. 跑一次异常账号巡检

提前 10 分钟排查,可能能省掉你后面 3 小时救火。

Gitea 遭遇 Spam 批量注册与恶意建仓:我的一次应急封禁与清理复盘
https://blog.sheyiyuan.com/posts/gitea-遭到-spam-恶意批量注册-批量建仓我的一次应急封禁与清理复盘/
作者
Sheyiyuan
发布于
2026-03-15 23:38
许可协议
CC BY-NC-SA 4.0
如果这篇文章对你有帮助,可以请我喝杯咖啡~