您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

员工写了个比删库更可怕的Bug!

时间:2024-03-26 15:20:48  来源:  作者:dbaplus社群

想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。

可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!

给大家分享一下(不是公开处刑),希望朋友们引以为戒。

一、Bug 起因

事情是这样的,昨天中午 11 点左右,突然用户群里的小伙伴反馈:自己直接成为了 鱼聪明 AI 网站 的管理员!

接下来,陆续有更多同学反馈:大家都成管理员了!

看到这里,我立刻就去查了下数据库,结果看到的是:

好家伙,早起脑供血不足的我立刻高血压上来了,怎么所有的用户都变成管理员了?!

我赶紧问下我所有的员工,这特么是谁干的!!!

然后员工小 A 大叫:“我 X,是我今天执行单元测试更新数据的时候,少加了个 where 条件!”

本来的预期:update user set userRole = 'admin' where id = 1

实际上执行:update user set userRole = 'admin'

于是导致整个库里的所有用户都变成了管理员,大家可以愉快地薅鱼毛了。

二、紧急处理

后来据这位写 Bug 的同学的回忆,由于她之前没有遇到过类似的情况,第一时间脑袋是一片空白、头嗡嗡的,完全不知道接下来要怎么做。

不过我是很冷静的,因为之前在公司处理过类似的情况,毕竟曾经凌晨 4 - 5 点的时候都被叫起来过。。。

所以立刻就给他发了一段处理方式:

解释一下,就跟我们在路上看到一起交通事故一样,第一时间要么是保护现场,放一个小牌牌不让大家进到事故发生地;要么就是防止扩大影响,人工疏导不让更多人围观、阻塞交通。

一般这两件事情是同时执行的,由于我知道怎么能够判定哪些用户本来是 VIP(比如通过 VIP 信息)、而且程序又有详细的日志,所以第一时间是让员工先把 user 表的所有角色设置为普通用户权限,防止有人继续利用管理员权限去做一些不好的事情。

接下来就是立刻停止了线上的前后端服务,一方面是为了后面好恢复数据,另外也是防止一些同学发现自己突然从会员变成了普通用户,增加大量的人工咨询成本。

所以当时很多同学访问鱼聪明时,看到了这样的截图:

稳定现场后,接下来就是想办法恢复数据到正常的状态,好在我给数据库设置了分钟级别的备份,可以直接把数据恢复到事故发生前的最近正常的时间点。

有了备份后的老数据,还要考虑恢复这个时间点后新增的用户数据。

有很多种恢复策略,我优先选择了逻辑最简单的策略:直接更新用户 updateTime > '2023-07-20 10:00:00' 的数据,根据 id 点对点覆盖除了 userRole 之外的数据列;如果没有对应的 id,新增一条数据。也就是使用类似 saveOrUpdate 的方法。

理想很丰满,现实很残酷。万万没想到,由于 updateTime 是一个发生数据修改时自动更新的字段,导致所有的数据 updateTime 全是最新的,相当于要把数据库全量的数据都去比较一遍。

于是我的员工呢,写了类似下面这样的程序:

然后就开始执行了,结果执行了很久很久,数据都没更新完。

看来单线程还是太慢了,于是我用并发编程的方式改进了同步的过程。先把所有用户分组,然后多线程同时执行 saveOrUpdateBatch 方法。

示例代码如下:

void restoreUserTable() {

List<User> userList = userService.list();

List<UserBak> userBakList = userList.stream().map(user -> {

user.setUserRole(null);

UserBak userBak = new UserBak();

BeanUtils.copyProperties(user, userBak);

return userBak;

}).collect(Collectors.toList());

int batchSize = 1000;

// 使用 lambda 表达式将 userList 每1000个元素分为一组

List<List<UserBak>> groupedBakUsers = IntStream.range(0, userList.size())

.boxed()

.collect(Collectors.groupingBy(index -> index / batchSize)) // 将索引按组分组

.values()

.stream()

.map(indices -> indices.stream()

.map(userBakList::get) // 根据索引获取 User 对象

.collect(Collectors.toList())) // 每组1000个元素的列表

.collect(Collectors.toList()); // 所有分组的列表

List<CompletableFuture<Void>> completableFutureList = new ArrayList<>();

int i = 1;

for (List<UserBak> groupedBakUser : groupedBakUsers) {

int finalI = i;

CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {

boolean b = userBakService.saveOrUpdateBatch(groupedBakUser, batchSize);

});

i++;

completableFutureList.add(completableFuture);

}

CompletableFuture.allOf(completableFutureList.toArray(new CompletableFuture[]{})).join();

}

使用这种方式,很快数据就恢复完成了。

当然,还有更简单的方式,比如联表查询、对比哪些数据行发生了变动,再去做修改。只不过当时情况紧急、再加上数据库量级可控,我们选择了相对理解成本最低的方式。

之后,我这边又手动做了一次全量备份,并且思考了一下还有没有遗漏的问题,才恢复上线。

三、事后复盘

整个事故时长接近 2 个小时,大致分为:

  • 人工发现事故(30 分钟后通过用户反馈才得知)
  • 定位问题(5 - 10 分钟)
  • 策略制定和同步(5 - 10 分钟)
  • 数据备份恢复(15 分钟)
  • 增量数据同步(40 分钟)
  • 上线前备份(10 分钟),同时进行其他考虑

从某种意义上来说,这次的事故比直接删库更严重!因为删库了赶紧恢复就好,但这次不仅出现了 “数据污染”,还出现了 “越权” 的问题,我们网站内仅管理员可见的敏感信息会存在泄露风险。好在我们也没什么敏感信息哈哈。

还有就是用户可能会利用漏洞来薅鱼毛(管理员可以大量获取),但经过我们的统计,这段时间利用漏洞薅鱼毛的人数寥寥无几,大家都是非常善良的,这才放下心来。

虽然这次的事故带来的损失不是特别大,但也发现了我们系统存在的问题。

我也跟这位员工说:出了事情不可怕,可怕的是不知道改正,出现同样的事情。

那么应该如何防止出现类似的事故呢?

1、控制操作权限

为了防止用户执行 update、delete 操作时不小心漏掉了 where 条件、直接更新全量数据,企业中一般是会禁止不带 where 条件的修改操作的。

出现这次的事故后,我也立刻给 MySQL 开启了 sql_safe_updates 配置:

缺少 where 条件的更新会直接触发下列报错:

之前为什么没加?主要是因为以前都是自己一个人开发系统,而且会有需要全量更新的场景,图省事儿。

2、生产环境隔离

正常情况下,不应该允许直接在本地连接和操作线上数据库的数据。而是需要先编写代码、提交代码审核、发布上线后,再执行修改操作。

像这次的事故,如果员工不是本地直接更新数据库,而是提交代码给我看一下,我大概率就会发现他少写了更新条件,就能防止了。

其实之前在腾讯的时候,我都会严格注意这些事项的。但之所以现在自己公司的项目是允许员工在本地连接线上的,想必大家也能猜到原因 —— 业务规模小、人数少,直接在同一个库开发会方便一些。

但如果项目的规模上来了,一定要做好多套环境的隔离,本地环境、测试环境、预发布环境、线上环境都要严格区分了。

3、SQL 审批

之前在腾讯的时候,想要修改关键库的数据,不能直接执行 SQL 语句,而是要先把 SQL 语句提交到审核平台,等你的领导和数据库运维确认没问题后,才能执行。这样每条 SQL 都是至少有 2 个人看过的,能够大大增加安全性。

曾经我觉得这种机制很麻烦,但经历过一些血泪教训后,才意识到这个环节真的是泰裤辣!

4、数据库审计

数据库审计是指记录和监控数据库的访问及 SQL 语句执行情况,从而精细化风险控制,提高数据安全性。

可以自己在数据库配置(比如开启日志、使用审计插件等),也可以使用第三方云服务自带的审计规则配置。

5、提升风险意识

最不需要技术,却也是最重要的一点,那就是要让团队的所有同学意识到这件事情带来的风险、问题的严重性。

因为你永远叫不醒一个装睡的人,同理,再多的防护也限制不了本身就想搞事的人。

所以这件事情是我和这位员工共同的责任,作为惩罚,我们决定请其他同事喝奶茶。就这么愉快地决定了~

不过也有做的好的地方,比如做了完整又灵活的数据备份,这是线上项目必备的操作。

以上就是本期分享,希望大家不仅是看个乐,也能有一些收获和启发,不过希望大家都不要遇到这类闹心的事情。

作者丨程序员鱼皮

来源丨公众号:程序员鱼皮(ID:coder_yupi)



Tags:Bug   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  Search: Bug  点击:(5)  评论:(0)  加入收藏
MySQL:BUG导致DDL语句无谓的索引重建
对于5.7.23之前的版本在评估类似DDL操作的时候需要谨慎,可能评估为瞬间操作,但是实际上线的时候跑了很久,这个就容易导致超过维护窗口,甚至更大的故障。一、问题模拟使用5.7.22...【详细内容】
2024-03-26  Search: Bug  点击:(8)  评论:(0)  加入收藏
拼多多先用后付,一直不付钱会怎样?看完网友评论都是卡BUG的
今天给大家带来了一篇重磅推文!话题就是关于拼多多的先用后付服务,如果一直不付钱会有怎样的后果?快来一起揭开这个让人好奇的谜团吧!众所周知,拼多多作为国内最大的农村电商平台...【详细内容】
2023-12-24  Search: Bug  点击:(62)  评论:(0)  加入收藏
Kubernetes 的调试功能 ,别慌:debug 不行,还有superdebug
这篇内容主要探讨了 Kubernetes 的调试功能,介绍了 kubectl debug 和 kubectl superdebug。它们支持容器挂载并且能够调试一些需要排查问题的 Pod。文章指出了在 Kubernetes...【详细内容】
2023-12-06  Search: Bug  点击:(210)  评论:(0)  加入收藏
分享30个避免低级Bug的代码技巧清单!
作为Java开发人员,希望确保程序没有错误。Bug不仅会给用户造成困扰,而且修复起来耗时且降低了自己的信任度。以下是30个代码技巧清单,一起分享。 使用变量之前进行初始化在Jav...【详细内容】
2023-10-03  Search: Bug  点击:(320)  评论:(0)  加入收藏
调试心得:通过观察正常的程序行为来识别Bug
有时候,当我调试一个问题的时候,我会特意忽略掉某些线程。这个时候,有人就问了:”这些线程是干什么的?你为什么知道要忽略它们?”我的回答是:我也不清楚这些线程是干啥的,但是无论它...【详细内容】
2023-09-08  Search: Bug  点击:(280)  评论:(0)  加入收藏
苹果用户险被电话诈骗160万:IOS有BUG
FaceTime诈骗事件近日在上海再次引发关注。一名女子几乎被骗走160余万元的存款,幸亏警方紧急止付才未造成损失。据报道,骗子利用FaceTime服务,通过盗取苹果ID信息获取被害人的个人资料,并冒充金融平台客服,以取消虚假贷款...【详细内容】
2023-07-10  Search: Bug  点击:(242)  评论:(0)  加入收藏
iOS 17体验评测:20条Bug 9个变化 不值得升级
北京时间6月6日凌晨,在2023年的WWDC开发者大会上,苹果带来了全新的iOS 17操作系统,我们也在第一时间给大家分析了这次全新的iOS 17带来的九大升级,感兴趣的朋友可以去翻看一下我...【详细内容】
2023-06-09  Search: Bug  点击:(177)  评论:(0)  加入收藏
故障案例:MySQL唯一索引有重复值,官方却说This is not a bug
一、问题:MySQL5.7.38主从架构,主节点唯一索引上(唯一索引不是主键)有重复值,全部从节点报1062,SQL线程状态异常,根据SQL线程报的binlog位置点,insert 数据时有重复值,插入失败二、原...【详细内容】
2023-06-01  Search: Bug  点击:(229)  评论:(0)  加入收藏
JavaScript 的 Anti-Debugging 技術
JavaScript 運行在客戶端,多數 Browser 亦有很強的 debugger,有時為了保護程式碼的邏輯不被破解或想要藏惡意程式之類的,會想辦法讓分析者沒辦法輕易分析原始碼。通常又可以分...【详细内容】
2023-05-20  Search: Bug  点击:(366)  评论:(0)  加入收藏
▌简易百科推荐
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(5)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(12)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(8)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(10)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(8)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
Kubernetes 究竟有没有 LTS?
从一个有趣的问题引出很多人都在关注的 Kubernetes LTS 的问题。有趣的问题2019 年,一个名为 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提...【详细内容】
2024-03-15  云原生散修  微信公众号  Tags:Kubernetes   点击:(5)  评论:(0)  加入收藏
站内最新
站内热门
站内头条