AI 代码生成供应链安全实战:Slopsquatting 依赖投毒与防御指南
一句话结论:Slopsquatting 是利用 LLM 的"概率性幻觉"自动生成恶意包名的新型攻击,传统 TypoSquatting 防御完全无效,你需要一套全新的供应链安全策略。
一次让我后背发凉的实验
上个月我做内部工具,需要把 Markdown 转成 Notion 格式。随手敲了个 prompt:
"用 Python 写一个 md2notion 转换器"
Copilot 秒回代码,import 第一行:
from md2notion import convert
我差点直接 pip install md2notion。
等等——PyPI 上真有 md2notion 吗?还是 AI 捏造出来的?
手动查了一下 PyPI,md2notion 在 2023 年确实被上传过——但不是官方包,而是一个安全研究员为了验证漏洞概念发的 PoC。
这就是 Slopsquatting。
Slopsquatting 是什么?
Slopsquatting(Slop + Squatting)是 2024 年底安全研究员 Bill Demirkapi 提出的概念。
传统 TypoSquatting 依赖用户打错字(比如 requsts 而不是 requests),Slopsquatting 依赖 LLM 凭空生成一个不存在的包名。
LLM 本质上是概率模型。你说"用某某库"时,它有很大概率:
- 编造一个听起来合理但实际不存在的库名
- 捏造一个确实存在的包名,但版本或函数签名是错的
- 生成一段包含虚构依赖的代码
攻击者只需要做两件事:
- 用自动化工具扫描 LLM 输出中高频出现的虚构包名
- 抢注这些包名,上传带恶意代码的包
等你复制 AI 生成的代码、直接 pip install,恶意代码就在本地执行了。
我在真实项目中踩到的坑
坑 1:虚构的 Helm Chart
写 K8s 部署脚本时我问 GPT:
"Helm 安装 Kafka 的典型配置"
AI 返回:
helm repo add kafka-repo https://charts.bitnami.com/bitnami
helm install my-kafka kafka-repo/kafka
Bitnami 的 Kafka chart 实际叫 bitnami/kafka,不是 kafka-repo/kafka。这个例子不算危险(只是报错),但如果生成的是一个看起来完全合理的私有仓库地址呢?
坑 2:PyPI 上的 ghost 包
有人扫了 ChatGPT 输出里高频出现的虚构 Python 包名,然后在 PyPI 上抢注了它们。包括:
phoenix-ai— 被下载 9000+ 次后才下架llm-tools— 内置反向 shell
这些包名之所以能命中,是因为多个 LLM 在不同问题的回答中,"独立地"生成了相同的虚构名字。
如何自动检测 Slopsquatting 风险
下面是我写的一个小工具,用来扫描项目中 AI 生成代码的依赖风险。
#!/usr/bin/env python3
"""
slopsquat-scanner.py — 扫描项目中 AI 生成代码的依赖投毒风险
"""
import ast
import re
import sys
import subprocess
from pathlib import Path
from typing import Set, Tuple
# 已知的 PyPI 官方包列表(可扩展)
# 实际使用时应从 PyPI JSON API 实时查询
KNOWN_PACKAGES = {
"requests", "numpy", "pandas", "flask", "django", "fastapi",
"torch", "tensorflow", "transformers", "langchain", "openai",
"anthropic", "pydantic", "sqlalchemy", "httpx", "aiohttp",
}
def extract_imports(filepath: Path) -> Set[str]:
"""从 Python 文件提取所有 import 的顶层包名"""
imports = set()
try:
tree = ast.parse(filepath.read_text(encoding="utf-8"))
for node in ast.walk(tree):
if isinstance(node, ast.Import):
for alias in node.names:
top_level = alias.name.split(".")[0]
imports.add(top_level)
elif isinstance(node, ast.ImportFrom):
if node.module:
top_level = node.module.split(".")[0]
imports.add(top_level)
except SyntaxError:
pass
return imports
def check_pypi_exists(package: str) -> Tuple[bool, str]:
"""查询 PyPI 确认包是否存在"""
try:
result = subprocess.run(
["pip", "index", "versions", package],
capture_output=True, text=True, timeout=10
)
if "WARNING: No matching distribution" in result.stderr:
return False, "NOT_FOUND"
if result.returncode == 0:
return True, "EXISTS"
return False, "UNKNOWN"
except subprocess.TimeoutExpired:
return False, "TIMEOUT"
def scan_project(project_dir: str):
"""扫描项目中的所有 Python 文件"""
path = Path(project_dir)
py_files = list(path.rglob("*.py"))
all_imports = {}
for pf in py_files:
imports = extract_imports(pf)
if imports:
all_imports[pf] = imports
all_packages = set()
for imports in all_imports.values():
all_packages.update(imports)
unknown = all_packages - KNOWN_PACKAGES
print(f"扫描文件数: {len(py_files)}")
print(f"发现第三方包: {len(all_packages)}")
print(f"未知包(需验证): {len(unknown)}")
print("-" * 50)
for pkg in sorted(unknown):
exists, status = check_pypi_exists(pkg)
if status != "EXISTS":
print(f"⚠️ {pkg} — 在 PyPI 上不存在!可能为 AI 虚构包")
for pf, imps in all_imports.items():
if pkg in imps:
print(f" 引用位置: {pf}")
else:
print(f"✅ {pkg} — 已确认")
print("-" * 50)
print("扫描完成")
if __name__ == "__main__":
if len(sys.argv) != 2:
print(f"用法: {sys.argv[0]} <项目目录>")
sys.exit(1)
scan_project(sys.argv[1])
使用方式:
python slopsquat-scanner.py /path/to/my/project
输出示例:
扫描文件数: 47
发现第三方包: 23
未知包(需验证): 3
--------------------------------------------------
⚠️ md2notion — 在 PyPI 上不存在!可能为 AI 虚构包
引用位置: src/converters/md2notion.py
✅ openai — 已确认
✅ pandas — 已确认
--------------------------------------------------
为什么传统防御失效了
传统供应链安全三板斧:
| 防御手段 | 对 TypoSquatting | 对 Slopsquatting |
|---|---|---|
| 拼写检查 | ✅ 有效 | ❌ 无效 |
| 包名信誉库 | ✅ 有效 | ❌ 无效 |
| 锁文件 | ⚠️ 部分有效 | ❌ 无效 |
| 代码审查 | ✅ 有效 | ⚠️ 看审查者经验 |
关键区别:TypoSquatting 的恶意包名和合法包名相似,Slopsquatting 的恶意包名没有合法对照物。你不能通过"它和谁长得像"来判断风险。
我现在的防御清单
踩完这些坑,我给自己定了几条铁律:
1. 安装前先查询
pip install 之前,先确认包存在:
# 方法一:pip 查询
pip index versions <package-name>
# 方法二:PyPI API
curl -s https://pypi.org/pypi/<package-name>/json | jq .info.name
2. 对 AI 生成的 import 保持怀疑
每次看到 AI 生成 from xxx import yyy,我问三个问题:
- 这个 xxx 是标准库吗?
- 如果是第三方库,它在 PyPI 上存在吗?
- 如果是公司内部库,它在私有 registry 里吗?
3. 用 virtual environment + freeze 锁定版本链
python -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
pip freeze > requirements.lock
锁文件的好处:就算有人抢注了包名,你不会在不知情下拉到新版本。
4. 写一个 CI 检查
# .github/workflows/dependency-scan.yml
name: Dependency Security Scan
on: [pull_request]
jobs:
slopsquat-scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- name: Install dependencies
run: pip install -r requirements.txt 2>&1 | tee install.log
- name: Check for installation errors
run: |
if grep -q "No matching distribution" install.log; then
echo "::error::发现可能为 AI 虚构的依赖包,请检查!"
exit 1
fi
5. 优先使用官方/知名源
pip install --index-url https://pypi.org/simple/ <package>
生态层面的防御进展
行业已经注意到这个问题了:
- PyPI 管理员开始主动下架 Slopsquatting 包,2024 年下架了 200+ 个
- Socket.dev 等供应链安全工具新增了 Slopsquatting 检测规则
- GitHub Copilot 在部分场景中增加了包名存在的标注
- OpenAI 在他们的安全报告中承认了这个问题,正在研究 mitigation
但坦白说,这些还不够。攻击者也在进化——他们不再只是抢注包名,而是:
- 先上传无害的包("镜像预置")
- 等它被广泛引用后
- 在后续版本中注入恶意代码
经典的"依赖混淆"攻击在 AI 时代的变种。
延伸思考
写这篇文章时我在想:如果 AI 生成的代码比例达到 80%,供应链安全会变成什么样子?
今天我们还能人工审查每个 import,但当项目中 80% 的代码是 AI 生成的,人工审查就变成了抽样审查。攻击者只需要在你没注意的一个 import 里做手脚。
我觉得最终的解不是"更严格的审查",而是代码来源的可信链路——就像软件供应链的 SLSA 框架一样,未来可能需要对每一行 AI 生成代码做"来源证明":这个包名是由哪个模型、在什么 prompt 下生成的,是否经过了安全验证。
对今天的你来说,最实用的建议只有一个:AI 写的 import,先查 PyPI,再 pip install。 多花 10 秒,省掉一个通宵的排查。

评论已关闭!