我录入了 14 亿条数据泄漏信息,搓了一个个人信息“泄漏”检测工具

张开发
2026/4/21 5:14:00 15 分钟阅读

分享文章

我录入了 14 亿条数据泄漏信息,搓了一个个人信息“泄漏”检测工具
我录入了 14 亿条数据泄漏信息搓了一个个人信息“泄漏”检测工具在这个数据裸奔的时代我们的手机号、邮箱、身份证信息可能早已在暗网被明码标价交易了无数次。作为一名技术人员面对“数据泄漏”这个沉重的话题与其焦虑不如做点实事。最近我利用公开传播的泄漏数据花了几天时间“搓”了一个个人信息泄漏检测工具。目前该工具已上线收录了约14 亿条泄漏数据。工具地址https://breach.garinasset.com/本文将不仅介绍这个工具的使用更想从技术架构的角度详细拆解如何从零构建一个亿级数据量的查询系统以及在隐私保护与查询效率之间如何做权衡。一、 为什么要造这个轮子市面上已经有类似“Have I Been Pwned”这样的国际知名服务国内也有一些安全厂商提供类似查询。但作为开发者我有着自己的执念数据本土化很多国际平台收录的数据主要是英文互联网生态针对国内社交平台、快递物流、历史社工库的数据覆盖不全。隐私零信任我不希望查询行为本身造成二次泄漏。很多查询工具虽然免费但难保它们不会记录你的查询内容甚至将你输入的手机号加入它们的营销数据库。技术挑战如何在一个轻量级架构下实现 14 亿条数据大约 500GB 的原始文本的秒级查询基于这三点我开始了这个项目的开发。二、 核心设计理念隐私至上在写第一行代码之前我确立了两个不可逾越的红线这也是本工具区别于市面上很多“野鸡查询”的核心特征“不记录”查询日志服务器内存不落盘查询即销毁。“不提供”隐私明文用户只能查询“是否泄漏”无法查看泄漏的具体密码或详细内容。2.1 为什么不提供明文假设系统允许用户输入手机号返回该手机号对应的明文密码。这看似是“验证功能”实则是“黑客帮凶”。任何恶意攻击者都可以利用此接口批量撞库获取他人的账号密码。因此本工具的返回结果设计为模糊匹配✅ 返回您的手机号在 X 个数据集中被发现。✅ 返回相关数据源涉及“某平台泄露”、“某快递数据”等标签。❌ 拒绝返回具体的密码哈希或明文。这种设计既满足了用户自查风险的需求又彻底切断了工具被滥用的可能性。三、 技术架构选型14 亿数据的承载之道处理 14 亿条记录传统的LIKE查询肯定是不行的。即使加了索引MySQL 在面对这种量级的文本检索时也会显得力不从心。我们需要的是倒排索引和列式存储的思想。3.1 技术栈选择后端语言Golang (高性能协程并发处理能力强)存储引擎ClickHouse (列式存储数据库OLAP 领域的王者)缓存层Redis (Bloom Filter 布隆过滤器)前端Vue 3 Tailwind CSS (轻量化)3.2 为什么选择 ClickHouse对于这种“写一次读多次”的场景ClickHouse 是绝佳选择。它的优势在于极高的压缩比原始数据 500GB导入 ClickHouse 后可能仅占用 50GB 左右极大地节省了磁盘空间。极致的查询性能ClickHouse 利用向量化执行引擎单表查询性能极强。![配图展示系统整体架构图包含前端用户输入、API 网关、Redis 布隆过滤器层、ClickHo[配图展示系统整体架构图包含前端用户输入、API 网关、Redis 布隆过滤器层、ClickHouse 存储集群的请求流向]四、 数据清洗与入库实战拿到原始数据通常是 CSV、TXT 或 SQL dump后第一步是清洗。原始数据往往脏乱差格式不统一。4.1 数据预处理脚本我们需要将不同格式的数据统一标准化。例如提取出手机号、邮箱、身份证号并打上来源标签。# 伪代码示例数据清洗与标准化importreimportcsvdefnormalize_data(raw_file_path,output_path):phone_patternre.compile(r1[3-9]\d{9})email_patternre.compile(r[a-zA-Z0-9_.-][a-zA-Z0-9-]\.[a-zA-Z0-9-.])withopen(raw_file_path,r,encodingutf-8,errorsignore)asf_in,\open(output_path,w,newline,encodingutf-8)asf_out:writercsv.writer(f_out,delimiter\t)writer.writerow([keyword,type,source_tag,leak_date])forlineinf_in:# 提取手机号phonesphone_pattern.findall(line)forphoneinphones:writer.writerow([phone,phone,Source_A,2023-01-01])# 提取邮箱emailsemail_pattern.findall(line)foremailinemails:writer.writerow([email,email,Source_A,2023-01-01])# 注意实际生产环境中需要多进程并行处理并处理文件编码问题4.2 ClickHouse 表结构设计为了查询效率我们需要合理设计分区和索引。CREATETABLEbreach_data(keyword String,-- 查询关键词手机号/邮箱/身份证typeEnum8(phone1,email2,id_card3),-- 数据类型source_tag String,-- 数据来源标签leak_dateDate,-- 泄漏日期近似insert_timeDateTimeDEFAULTnow())ENGINEMergeTree()PARTITIONBYtoYYYYMM(leak_date)-- 按月分区ORDERBY(keyword,type)-- 主键排序这是查询速度的关键SETTINGS index_granularity8192;关键点解析ORDER BY (keyword, type)ClickHouse 会按照这个顺序存储数据。由于我们的查询场景主要是WHERE keyword xxx这种排序方式可以让数据在物理存储上连续极大地加速范围扫描。PARTITION BY按月分区可以方便地删除或归档旧数据同时减少查询时的文件扫描范围。4.3 批量导入性能优化导入 14 亿条数据一条条 Insert 肯定会慢到让人崩溃。ClickHouse 官方推荐使用INSERT SELECT或者批量 CSV 导入。# 使用 clickhouse-client 导入 TSV 文件catcleaned_data.tsv|clickhouse-client--queryINSERT INTO breach_data FORMAT TabSeparated在导入过程中建议暂时关闭表的 MergeTree 后台合并线程system.stop_merges待数据全部导入后再开启可以显著提升导入速度。五、 查询性能优化布隆过滤器的应用虽然 ClickHouse 查询很快但面对海量请求直接穿透到数据库依然有风险。而且绝大多数查询可能都是“未命中”的用户想知道是否泄漏往往是因为还没泄漏。为了保护后端数据库我们引入了Redis 布隆过滤器。5.1 布隆过滤器原理布隆过滤器是一种空间效率极高的概率型数据结构用于判断“一个元素是否在一个集合中”。如果它说**“不存在”则该元素一定不存在**。如果它说**“存在”则该元素可能存在**有极小的误判率。5.2 实现逻辑我们在数据入库后将所有keyword预热加载到 Redis 的布隆过滤器中。// Golang 伪代码查询逻辑funcCheckLeak(keywordstring)(*Result,error){// 1. 第一层Redis 布隆过滤器exists,err:redisClient.BFExists(breach_bloom_filter,keyword).Result()iferr!nil{returnnil,err}// 如果布隆过滤器说不存在那肯定没泄漏直接返回无需查库if!exists{returnResult{IsLeaked:false},nil}// 2. 第二层ClickHouse 精确查询// 此时虽然布隆过滤器说存在但有可能是误判需要去数据库确认varcountuint64query:fmt.Sprintf(SELECT count() FROM breach_data WHERE keyword %s,keyword)// 执行查询...ifcount0{// 确实泄漏了获取详情但不返回密码returnResult{IsLeaked:true,Sources:getSources(keyword)},nil}// 布隆过滤器误判的情况returnResult{IsLeaked:false},nil}通过引入布隆过滤器大约90%的“未命中”查询在 Redis 层就被拦截了极大地降低了 ClickHouse 的负载响应速度也从几十毫秒降低到了几毫秒。[配图展示查询请求的链路耗时分析图对比直接查询数据库与经过布隆过滤器拦截后的性能差异]六、 隐私保护机制的深度实现前面提到了“不记录查询记录”这不仅仅是口头承诺更需要在代码层面强制执行。6.1 日志脱敏与禁用在 Nginx 或应用服务器的配置中我们需要对日志进行特殊处理。Nginx 配置示例location /api/check { # 不记录访问日志或者仅记录状态码不记录 request_body access_log off; # 或者使用自定义日志格式过滤掉敏感参数 # log_format privacy $remote_addr - $status; proxy_pass http://backend_service; }应用层逻辑在 Golang 的 Gin 框架中我们可以编写中间件确保任何 Panic 或 Info 日志都不包含 Request Body。funcPrivacyShieldMiddleware()gin.HandlerFunc{returnfunc(c*gin.Context){// 读取 Body 后立即消费掉防止日志框架自动打印bodyBytes,_:ioutil.ReadAll(c.Request.Body)c.Request.Bodyioutil.NopCloser(bytes.NewBuffer(bodyBytes))// 将敏感字段置空后再打印日志如果必须打印请求体// 这里我们选择完全不打印请求体log.Println(Received request from:,c.ClientIP())c.Next()}}6.2 接口限流防爬虫为了防止恶意爬虫利用本接口进行批量枚举我们实施了严格的限流策略。IP 维度每个 IP 每分钟限制查询 10 次。验证码机制连续查询失败或频率过高强制触发图形验证码。// 使用 golang.org/x/time/rate 进行令牌桶限流varlimiterrate.NewLimiter(10,5)// 每秒产生 10 个令牌桶容量 5funcRateLimitMiddleware()gin.HandlerFunc{returnfunc(c*gin.Context){if!limiter.Allow(){c.JSON(http.StatusTooManyRequests,gin.H{error:请求过于频繁请稍后再试})c.Abort()return}c.Next()}}七、 前端交互与用户体验前端设计力求极简避免花哨的动画专注于核心功能。输入框支持手机号、邮箱、身份证自动识别。结果展示绿色恭喜未发现泄漏记录。红色警告发现泄漏记录并列出涉及的平台数量。为了让用户更直观地理解风险我们还加入了一个简单的风险评分算法。如果泄漏数据中包含身份证号风险等级设为“高危”如果仅包含手机号设为“中危”。八、 实测与数据展示经过几天的调试和上线运行目前系统表现稳定。数据量1,458,230,114 条记录。存储空间原始数据约 480GBClickHouse 压缩后约 42GB。平均查询延迟P95 30ms包含网络延迟。并发能力单机支持 QPS 2000得益于布隆过滤器。典型案例用户输入138****1234。系统返回⚠️检测结果发现泄漏您的信息在3个公开数据集中被发现。涉及标签某电商平台(2019)、某物流快递(2021)。建议请立即更改相关平台密码并开启双重验证(2FA)。九、 总结与未来展望开发这个工具的初衷是希望在数据泄漏的洪流中给普通人一个确认风险、提升安全意识的机会。技术总结ClickHouse是处理海量数据查询的利器压缩比和查询性能令人惊艳。布隆过滤器是高并发查询场景下的“守门神”能有效解决空查询穿透问题。隐私保护必须贯穿架构设计的始终从日志到接口返回值都要时刻警惕。未来计划增加域名监控功能用户可以监控自己域名的邮箱是否泄漏类似 Google Postmaster Tools。开发API 接口提供给企业安全团队调用用于内部员工账号风险检测。安全提示本工具仅收录公开传播的泄漏数据不保证覆盖所有泄漏事件。查询结果仅供参考请勿将此作为唯一的安全评估依据。如果你觉得这个工具有用欢迎访问 https://breach.garinasset.com/ 进行体验也欢迎各位技术同仁提出宝贵的改进建议。保护数据安全我们一直在路上。

更多文章