史上最大规模数据泄露:20亿邮箱与13亿密码的索引与验证

本文详细介绍了Have I Been Pwned如何处理并索引一个包含近20亿唯一邮箱地址和13亿密码的庞大数据集,涵盖了数据验证过程、技术处理挑战、通知发送策略以及对公众的安全建议。

Troy Hunt: 20亿邮箱地址遭泄露,我们已将其全部索引至Have I Been Pwned

我讨厌关于数据泄露的夸张新闻标题,但“20亿邮箱地址”这个标题若要说是夸张,那它必须是夸大或言过其实的——而它并不是。它从一个更精确的数字19.57476021亿个唯一邮箱地址向上取整,但除此之外,它完全名副其实。哦,还有13亿个唯一密码,其中6.25亿个我们以前也从未见过。这是我们迄今为止处理过的规模最大的数据集,而且优势显著。

编辑: 为了明确数据的来源以及Synthient(你将在下一段读到)的角色:这些数据来自网络犯罪分子发布它的众多地点。Synthient(由Ben在他大学最后一年运营)索引了这些数据,并将其提供给Have I Been Pwned,唯一目的是通知受害者。他是照亮坏人的好人,所以在阅读时请记住这一点。(Ben收到的一些反馈正是我在本文最后一段所预见的。)

几周前,我写过关于Synthient在其威胁情报平台中索引并与我们共享的1.83亿个唯一邮箱地址。我解释说,这只是他们索引的数据集的一部分,并且不包括凭据填充记录。窃取器日志数据是通过在受感染机器上运行的恶意软件获取的。相比之下,凭据填充列表通常源自其他暴露了邮箱地址和密码的数据泄露事件。然后它们被打包、出售、重新分发,并最终用于登录受害者账户。不仅限于它们最初泄露的账户,因为人们一遍又一遍地重复使用相同的密码,一次泄露的数据经常可以用于完全无关的网站。一个关于猫咪评论论坛的泄露,暴露的数据随后可能被用于登录受害者的购物、社交媒体甚至邮箱账户。在这方面,凭据填充数据变成了“城堡的钥匙”。

让我概述一下我们如何验证数据,你可以为此做些什么,以及对技术爱好者来说,我们为处理如此大规模的数据必须克服的一些困难。

数据验证

我验证的第一个人数据很容易——我自己😔 一个我从90年代就有的旧邮箱地址以前就在凭据填充列表中出现过,所以并不太意外。此外,我找到了一个与我地址关联的密码,我肯定在许多年前用过它,而且它和那个时代你预期的一样糟糕。然而,与我地址关联的其他密码都不熟悉。它们看起来确实像是其他人可能用过的密码,但我很确定它们不是我的。其中一个甚至只是来自国家另一端珀斯的一个IP地址,这作为我可能用过的密码来说既不可行,又诡异得近乎巧合。我的意思是,在世界上所有可能出现IP地址的地方,它偏偏来自我以前去过多次的自己国家的一个地方……

接下来联系HIBP订阅者,我联系了少数几位并请求他们协助验证数据。我选择了一些从未出现在我们见过的任何数据泄露中的订阅者;我上面的经验表明其中存在回收利用的数据,并且我们之前在调查其他事件时已验证过这一点。然而,这些全新的数据是合法的吗?我收到的第一个回复正是我所寻找的:

#1 是一个我不再使用的旧密码。#2 是一个较新的密码。谢谢提醒,我已经去更改了所有使用其中任何一个密码的关键账户的密码。

完美地说明了大多数人在密码方面的行为,上面提到的#2只是#1后面加了两个感叹号!!(顺便说一下,这些都是简单的六位和八位字符密码,而且它们都不在Pwned Passwords中。)他总共有三个密码,这也意味着其中一个,就像我的数据一样,并不熟悉。然而,这里最重要的是,这个例子完美地说明了为什么我们要努力处理这样的数据:#2 是一个真实、当前正在使用的密码,这个人正在积极使用它,而它就与他的邮箱地址并列,在犯罪分子之间传递。然而,通过这项努力,这对凭据现在已经变得无用,这正是我们进行这项工作的目标,只是规模是几十亿倍。

第二位受访者只有一个密码与其地址关联:

是的,那是我在20年到10年前之间,用于我称之为一次性或不重要账户的密码。

那个密码也只有八位字符,但这次,我们以前在Pwned Passwords中见过它很多次。关于密码年代的观察与我自己的记录一致,所以里面肯定有一些相当古老的数据。

接下来的回复一点也不奇怪:

我对那个密码很熟悉……我差不多10年前用过它……记不清最后一次用它是什么时候了。

那也是在一个企业账户上,该地址的所有者适时地将我的邮件转发给了网络安全团队进行进一步调查。与这位女士邮箱地址关联的单个密码有九个字符之多,并且以前也未出现在Pwned Passwords中。

接下来是一位受访者,他/她回复了我的问题,所以我将在下面列出它们及相应的答案:

这个熟悉吗? 是的 你以前用过它吗? 是的,而且仍然在一些我不再使用的账户上。 如果是,多久以前? 不幸的是,它仍然在一些活跃账户上,我刚刚列了清单要立即更改或关闭。

这位受访者的八位字符密码包含大写、小写、数字和一个“特殊”字符,同样不在Pwned Passwords中。与前面的回复类似,那个密码仍在活跃使用中,对所有者构成了真实风险。它会通过大多数密码复杂度标准,并会绕过任何使用Pwned Passwords来阻止不良密码的服务,所以,这再次凸显了我们处理这些数据的重要性。

下一个人有三个不同的密码与其邮箱地址所在行关联,他们回复了一个现在常见的回答:

是的,这些很熟悉,最后一次使用是10年前。

我们实际上以前在Pwned Passwords中见过所有这三个密码,每个都出现过很多次。另一位受访者,密码正是你预期孩子会用的那种游戏玩家风格(其中一个我们以前没见过),也确认了(我想?)它们的使用:

可能是我小时候用的吧 lol

回答不是断然“是的,那是我的数据”的情况很少见。一个人名下的两个密码都在Pwned Passwords中(尽管每个只出现过一次),但这两个密码都有可能从未被这个特定个人使用过。也可能他们忘记了十多年前用过的密码,甚至可能是随后被泄露的服务自动分配给他们的。就把它当作一个统计异常值,但我认为值得提出来强调,出现在这个数据集中并不能保证你的真实密码被暴露。如果你的邮箱地址在这个数据集中被找到,那当然是真实的,所以数据中必定有真实成分,但这提醒我们,当数据从这么多不同的来源长时间聚合而来时,总会有一些不一致之处。

搜索Pwned Passwords

简单回顾一下,我们将密码加载到我们称之为Pwned Passwords的服务中。当我们这样做时,密码和它旁边出现的邮箱地址之间绝对没有关联。这是为了保护你,也保护我们自己;你能想象如果HIBP被攻破会怎样吗?这不是不可能,而暴露数十亿能立即解锁无数账户的凭据对的影响将是灾难性的。这风险极高,而且当你可以在不创建将其链接回某人的风险的情况下搜索独立密码时,这完全是不必要的。

想想看:如果你有一个密码“Fido123!”,并且发现它以前被暴露过(它确实有),那么它是针对你的邮箱地址还是别人的邮箱地址暴露的都无关紧要;它仍然是一个糟糕的密码,因为它以你的狗命名,后面跟着一个非常可预测的模式。如果你有一个真正强大的密码并且它在Pwned Passwords中,那么你可以带着一些信心离开,认为它确实是你的。无论如何,你都不应该在任何地方再次使用那个密码,而Pwned Passwords就完成了它的工作。

检查服务很容易、匿名,并且根据你的技术舒适度,可以通过几种不同的方式完成。以下是从上一篇Synthient博文复制粘贴的内容:

  • 使用Pwned Passwords搜索页面。密码受匿名模型保护,所以我们永远看不到它们(它在浏览器本身处理),但如果你有疑虑,只需检查你可能怀疑的旧密码。
  • 使用k-匿名性API。这是驱动前一个页面的方式,如果你擅长编写代码,这是一个简单的方法,并让你对匿名方面充满信心。
  • 使用1Password的Watchtower。这个密码管理器有一个内置的检查器,它使用上述API,可以检查你保管库中的所有密码。(披露:1Password是本博客的常规赞助商,并且在HIBP上有产品植入。)

撇开我对1Password的既得利益,Watchtower是了解你在此次事件中潜在暴露风险的最简单、最快捷的方式。如果你想知道为什么我有这么多易受攻击和重复使用的密码,这是多年来我保存的测试账户和一些服务强制你使用的4位数PIN码的组合。你相信吗?每一个曾经存在的4位数都已被攻破?!(如果你感兴趣,ABC有一个很棒的信息图,使用基于HIBP数据的热图,显示了4位数PIN码的一些非常可预测的模式。)

这不是Gmail泄露事件

我很不情愿说,但我必须说,鉴于几周前窃取器日志导致了荒谬、完全错误的头条新闻:

这个故事最近几个小时突然获得了更多的关注,我觉得需要澄清一件显而易见的事情:这不是Gmail泄露,它只是包含了感染恶意软件的受害者的凭据,而Gmail是占主导地位的邮箱提供商:https://t.co/S75hF4T1es — Troy Hunt (@troyhunt) 2025年10月27日

在这个最新的数据集中有3200万个不同的邮箱域名,其中gmail.com是其中之一。它当然是最大的,拥有3.94亿个唯一邮箱地址。换句话说,这个数据集中80%的数据与Gmail绝对无关,而那20%的Gmail地址也与谷歌方面的任何安全漏洞绝对无关。好了——现在让新闻报道回归理智吧!

技术细节

我想补充这部分,只是为了强调处理这些数据有多么痛苦。这个数据集的大小几乎是我们之前加载的最大泄露数据的3倍,而HIBP也比2019年我们加载Collection #1数据时大了许多倍。获取20亿条记录,并将我们尚未在现有150亿数据集中见过的那些添加进去,同时不影响每天为数百万访客服务的实时系统,绝非易事。管理SQL Server索引的细微差别以优化插入和查询,这可不是我心目中的乐趣,老实说,这是非常艰难的几周。这也是一个非常昂贵的时期,因为我们将云计算能力调到11(我们在Azure SQL超大规模上运行,我们将其最大配置为80核,持续了近两周)。

一个简单的挑战示例是,在将所有邮箱地址加载到暂存表后,我们需要为每个创建SHA1哈希。通常,这类似于“update table set column = sha1(email)”就完成了。但这完全崩溃了,所以我们最终做了“insert into new table select email, sha1(email)”。但在其他情况下,泄露加载要求我们对其他列进行更新(不创建哈希),在多次情况下,我们不得不在执行了一天多且看不到尽头后终止它。所以,我们最终在循环中分批处理(通常每次100万条记录),沿途报告进度,以便我们大致知道它何时真正完成。这是一个反复试验、漫长等待、出错然后采取完全不同方法的痛苦过程。

通知我们的订阅者是另一个问题。我们有590万订阅者,其中290万在此次数据中🫨 一次性发送这么多邮件是困难的。困难不在于发送它们本身,而在于避免最终进入声誉黑名单或被接收服务器限制邮件发送。过去在加载大型(尽管小得多)数据集时,这种情况发生过多次;例如,Gmail突然看到大量峰值并减慢到收件箱的递送速度。对于发送泄露通知来说问题不大,但对于尝试登录其仪表板却无法再接收包含“魔法”链接邮件的人来说,这就是一个大问题。

我们为此事件所做的是减缓单个泄露通知邮件的递送速度。虽然我最初打算在一周内以恒定速率发送邮件,但有人在我的周五直播中听我讲过后提出了一个更好的建议:

我发现处理大量邮件发送的最佳策略是,每次想要增加发送量时,查看过去30天发送邮件的平均数量,然后每天增加大约50%,直到处理完队列。

这很有道理,并且在我做更多研究后得到了证实(谢谢Joe!)。所以,以下是我们现在计划的递送时间表:

那是按小时分解的,每小时量增加1.015倍,这样邮件以类似、逐渐增加的节奏分散开来。按日计算,这在每24小时内增加45%,在Joe建议的50%阈值内。另外,我们显然还有其他所有机制,例如专用IP、正确配置的DKIM、DMARC和SPF、仅向双重确认订阅者发送邮件,以及友好的垃圾邮件邮件正文构造。所以,你可能需要几天才能收到通知,或者如果你不耐烦,可以随时在haveibeenpwned.com上搜索。

我们已经立即发送了所有域名通知邮件,因为根据定义,它们是发送到非常广泛的不同邮件服务器;只有个人通知邮件我们是分批缓慢发送的。

最后,如果你已将Pwned Passwords集成到你的服务中,你现在会看到明显更大的响应大小。我在开篇段落中提到的数字使每个哈希范围的大小平均增加了约50%,这将把响应从约26kb推高到40kb。这是在使用brotli压缩的情况下,所以,显然,请确保你的请求充分利用了压缩。

结论

这些数据现在可以在HIBP中作为“Synthient Credential Stuffing Threat Data”进行搜索。它与我之前提到的Synthient数据是完全独立的数据集;它们是离散的数据集,有一些交叉,但显然,这个要大得多。而且,当然,所有密码现在都可以按照上述Pwned Passwords指南进行搜索。

如果我能以一个请求结束:这对我们来说是一项极其费力、耗时且昂贵的任务。我们已尽力验证数据的完整性,并以实用的方式使其可搜索,同时尽可能保持以隐私为中心。发送如此多的通知将不可避免地导致大量的回复,人们想要访问完整的数据行,详细盘问我们确切的数据来源,或者,信不信由你,直接辱骂我们。不做这些事情会很好,我建议将精力投入到获取密码管理器、使密码强大且唯一(或者更好的是,在可用时使用通行密钥),以及开启多因素认证上。这对所有人来说都将是一个极好的结果😊

编辑: 我已关闭此博文的评论。正如你在下方将看到的,有一连串的问题已经在文章本身得到了回答,加上一些评论开始接近我在最后一段所预测的情况。阅读、回复和参与非常耗时,而且此时所有答案都已经在这里了,无论是上面的正文还是本编辑下方的评论中。

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计