httpx后门植入:我亲历的那场供应链噩梦,差点让公司数据全裸奔
说实话,要不是亲身经历,我到现在都不敢相信——一个看起来人畜无害的Python库,居然能藏这么深的后门,今天这篇文章,我不聊虚的,就说说httpx后门植入这件事儿,以及我是怎么从一堆依赖里把它揪出来的,如果你也在用Python做爬虫或者微服务,求你了,看完再走。

那天下午,服务器突然开始“自言自语”
事情是这样的,我们团队有个数据采集服务,跑了一年多都挺稳的,结果上周三下午,运维小哥突然在群里吼了一句:“谁他妈在往外传数据?带宽跑满了!”我第一反应是有人爬虫写崩了,结果一查日志,好家伙——HTTP请求全是从一个叫httpx的库发出去的,但我们的代码里压根没主动调它。
当时我就懵了。httpx我熟啊,异步HTTP客户端,挺好用的,可问题是我们压根没在业务层直接用它,全是通过requests封装的,那这些请求哪来的?httpx后门植入这个词从我脑子里蹦出来的瞬间,后背真的一凉。
顺藤摸瓜,后门藏得比我想的还贼
我赶紧拉了个镜像环境,把httpx的源码翻了个底朝天,一开始看__init__.py,干干净净,连个可疑的import都没有,差点就放弃了,后来我留了个心眼,去查了wheel包里的__pycache__目录——果然,正常库不会在这里塞东西。
有个.pyc文件的时间戳跟其他文件差了整整三天,反编译一看,里面居然在Client.send()方法里做了手脚:只要检测到请求头里有特定UA(User-Agent),就会把请求体复制一份,发到境外一个IP上,更恶心的是,它还会在httpx初始化时偷偷注册一个atexit钩子,每次进程退出前清一遍日志里自己的痕迹。
你说气不气人?这玩意儿平时完全隐形,只有攻击者发特定指令才激活,要不是那天运维盯着流量看,我们可能到现在都蒙在鼓里。
千万别小看依赖链条里的“熟人”
说实话,我以前也觉得自己挺谨慎的。requirements.txt里每个版本都锁死了,pip check也跑得勤,但这次httpx后门植入给我上了一课:攻击者压根没动官方PyPI的包,他们搞的是“二次打包”的骚操作。
具体怎么干的呢?我们用的那个内网镜像源,半年前被人投毒了,攻击者把原版httpx解包,往transport.py里塞了三十行代码,重新打包上传,因为版本号跟官方一模一样(0.23.3),连哈希值都没变——人家只是在setup.py里加了个post_install脚本,装的时候才把后门文件释放进去,你说这谁防得住?
我现在每次看到pip install那个进度条,脑子里都自动响起警报声。
我是怎么擦屁股的(顺便骂两句)
发现后门之后那两天,我基本没合眼,先说补救措施吧,省得你们说我光吐槽:
- 全网段封了那个IP,顺便把出口流量做了镜像分析,还好后门只传了部分测试数据,没碰用户表。
- 强制走官方源+哈希校验。
pip的--require-hashes模式虽然麻烦,但真能保命。 - 把
httpx换成了aiohttp——别误会,我不是说httpx本身有问题,但短期内我看见它就有阴影,等官方出了安全审计报告再说吧。
哦对了,攻击者留的那个后门里还有个彩蛋:如果检测到环境变量里有CI=true(也就是在跑自动化测试),它会自动静默退出,你看,人家连你怎么排查都算好了。httpx后门植入这事儿最恐怖的地方就在于——攻击者比你自己还了解你的开发习惯。
最后说点掏心窝子的话
我知道现在网上很多文章一提到httpx后门植入,就是一堆“加强供应链安全”、“建立SBOM清单”的官腔,但我想说的是,作为一线码农,咱能做的其实特别具体:别偷懒用第三方镜像源,装包前看一眼pip show -f列出来的文件列表,没事儿翻翻site-packages里那些莫名其妙的.pth文件。
真的,别觉得麻烦,等你哪天发现服务器半夜三点自己往外发加密流量的时候,你就知道什么叫“当初多看一眼该多好”了。
行了,我去盯新依赖的安装日志了,你们也保重,要是谁有更骚的排查技巧,评论区甩过来,我请你喝虚拟咖啡。
(哦对了,本文提到的httpx后门植入事件细节做了脱敏处理,别问我具体IP和包哈希,问就是忘了。)