🔍 开发记录:打造一个自动化的 GitHub AI API 密钥泄露扫描工具
📖 项目背景
最近在使用各种 AI 服务时,发现不少开发者会不小心将 API 密钥(OpenAI、Anthropic Claude 等)提交到 GitHub 上,这可能导致:
- 🚨 API 密钥被他人盗用
- 💸 产生巨额费用(有人因此损失几千美元)
- 🔐 账号安全风险
于是我决定开发一个自动化扫描工具,可以扫描到别人仓库的 api_key。
🎯 项目目标
- 自动化:基于 GitHub Actions,无需本地运行
- 智能检测:支持多种 AI API 密钥格式,减少误报
- 详细报告:生成完整的扫描报告,包含文件路径、行号、置信度评级
- 即时告警:发现问题自动创建 Issue 通知
- 完全免费:利用 GitHub Actions 的免费额度
🏗️ 技术架构
项目采用模块化设计,主要包含以下几个核心模块:
ai-scan/
├── config.py # 配置文件(API 密钥模式定义)
├── secret_detector.py # 敏感信息检测器
├── github_scanner.py # GitHub API 交互
├── scanner.py # 主扫描逻辑
├── report_generator.py # 报告生成器
├── scan_history.py # 扫描历史管理
├── scan_github.py # 命令行入口
└── .github/workflows/ # GitHub Actions 工作流
💡 核心实现
1. 敏感信息检测器
这是整个项目的核心,负责识别代码中的 API 密钥。我定义了 30+ 种检测模式,涵盖:
- OpenAI API Key(
sk-...、sk-proj-...) - Anthropic Claude(
sk-ant-...) - Google AI/Gemini(
AIza...) - 环境变量赋值(
OPENAI_API_KEY = "...") - 对象属性(
apiKey: "...")
关键代码:
# config.py - 部分检测模式
SENSITIVE_PATTERNS = [
# OpenAI API密钥格式
r'sk-[a-zA-Z0-9]{32,}',
r'sk-proj-[a-zA-Z0-9_-]{32,}',
# Anthropic API密钥格式
r'sk-ant-[a-zA-Z0-9_-]{32,}',
# Google AI (Gemini) API密钥格式
r'AIza[a-zA-Z0-9_-]{35}',
# 环境变量模式
r'OPENAI_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
r'ANTHROPIC_API_KEY[\s]*=[\s]*["\']?([a-zA-Z0-9_-]{20,})["\']?',
# camelCase 模式
r'apiKey[\s]*:[\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
r'openaiApiKey[\s]*[:=][\s]*["\']([a-zA-Z0-9_-]{20,})["\']',
]
# secret_detector.py - 核心检测逻辑
class SecretDetector:
def detect_secrets_in_text(self, text: str, file_path: str = "") -> List[Dict]:
"""在文本中检测敏感信息"""
findings = []
lines = text.split('\n')
for line_num, line in enumerate(lines, 1):
for pattern in self.patterns:
matches = pattern.finditer(line)
for match in matches:
secret = match.group(0)
# 过滤示例代码
if self._is_likely_example(line, secret):
continue
findings.append({
'file_path': file_path,
'line_number': line_num,
'line_content': line.strip(),
'secret': secret,
'pattern': pattern.pattern,
'confidence': self._calculate_confidence(secret, line)
})
return findings
def _is_likely_example(self, line: str, secret: str) -> bool:
"""判断是否可能是示例代码"""
line_lower = line.lower()
example_keywords = [
'example', 'sample', 'demo', 'test', 'placeholder',
'your_api_key', 'xxx', 'todo', 'replace', 'change_me'
]
for keyword in example_keywords:
if keyword in line_lower:
return True
return False
def _calculate_confidence(self, secret: str, line: str) -> str:
"""计算置信度"""
# 高置信度:密钥格式完整且不在注释中
if (secret.startswith('sk-') and len(secret) > 40 and
not line.strip().startswith('#') and
not line.strip().startswith('//')):
return 'high'
# 中等置信度:符合基本模式
if len(secret) >= 30:
return 'medium'
return 'low'
2. 智能扫描策略
为了避免重复扫描和提高效率,我实现了扫描历史管理:
# scanner.py - 主扫描逻辑
class CloudScanner:
def scan_ai_projects(self, max_repos: int = 50) -> str:
"""自动搜索并扫描AI相关项目"""
print(f"🚀 开始自动搜索 AI 相关项目")
scan_start_time = datetime.now()
# 定义过滤函数:检查仓库是否已扫描
def is_scanned(repo_full_name: str) -> bool:
return self.scan_history.is_scanned(repo_full_name)
# 搜索仓库,实时过滤已扫描的
repos_to_scan = self.github_scanner.search_ai_repos(
max_repos=max_repos,
skip_filter=is_scanned if self.skip_scanned else None
)
# 扫描所有仓库
all_findings = []
for idx, repo in enumerate(repos_to_scan, 1):
print(f"🔍 [{idx}/{len(repos_to_scan)}] 扫描仓库: {repo['full_name']}")
findings = self._scan_repository(repo, scan_type="auto:ai-projects")
all_findings.extend(findings)
# 生成报告
report_path = self.report_generator.generate_report(
all_findings, scan_start_time, scan_type="auto:ai-projects"
)
return report_path
3. 详细的扫描报告
报告生成器会创建结构化的 TXT 报告,包含:
# report_generator.py - 报告生成核心
class ReportGenerator:
def generate_report(self, scan_results: List[Dict],
scan_start_time: datetime,
scan_type: str = "auto") -> str:
"""生成扫描报告"""
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
filename = f"scan_report_{timestamp}.txt"
filepath = os.path.join(self.output_dir, filename)
with open(filepath, 'w', encoding='utf-8') as f:
# 报告头
f.write("╔" + "═" * 78 + "╗\n")
f.write("║" + " 🔒 InCloud GitHub 云上扫描器 - 扫描报告".ljust(78) + "║\n")
f.write("╚" + "═" * 78 + "╝\n\n")
# 按仓库分组显示结果
results_by_repo = self._group_by_repo(scan_results)
for repo_url, findings in results_by_repo.items():
self._write_repo_findings(f, repo_url, findings)
# 统计信息
self._write_statistics(f, scan_results)
return filepath
def _mask_secret(self, secret: str) -> str:
"""部分隐藏密钥"""
if len(secret) <= 8:
return "*" * len(secret)
# 显示前4个和后4个字符
return f"{secret[:4]}{'*' * (len(secret) - 8)}{secret[-4:]}"
报告示例:
╔══════════════════════════════════════════════════════════════════════════════╗
║ 🔒 InCloud GitHub 云上扫描器 - AI API Key 扫描报告 ║
╚══════════════════════════════════════════════════════════════════════════════╝
📋 扫描信息
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
🎯 扫描类型: 🤖 自动搜索 AI 项目
🔴 发现问题数: 3 个 (🔴 1 高危, 🟡 2 中危)
📦 涉及仓库数: 2 个
╭──────────────────────────────────────────────────────────────────────────────╮
│ 📦 仓库: user/ai-chatbot │
│ 🔗 地址: https://github.com/user/ai-chatbot │
│ 🔴 高危 发现 2 个问题 │
╰──────────────────────────────────────────────────────────────────────────────╯
┌─ 问题 #1 ──────────────────────────────────────────────────────────────────
│
│ 🔴 风险等级: 高危 - 立即处理
│
│ 📄 文件路径: src/config.py
│ 📍 行号: 15
│
│ 🔑 密钥类型: 🤖 OpenAI API Key (Project)
│ 🔐 密钥内容: sk-p****************************xyz
│ 🎯 匹配规则: 📌 OpenAI Project API Key 格式 (sk-proj-...)
│
│ 💻 代码片段:
│ OPENAI_API_KEY = "sk-proj-xxxxx..."
│
└──────────────────────────────────────────────────────────────────────────────
4. GitHub Actions 自动化
这是项目的一大亮点!无需本地运行,完全基于 GitHub Actions:
# .github/workflows/manual-scan.yml
name: AI API Key Scanner - Manual Scan
on:
workflow_dispatch:
inputs:
scan_type:
description: '扫描类型'
required: true
type: choice
options:
- 'auto - 自动搜索AI项目'
- 'user - 扫描指定用户'
- 'org - 扫描指定组织'
- 'repo - 扫描单个仓库'
max_repos:
description: '最大扫描仓库数'
type: number
default: 50
permissions:
contents: write # 允许提交报告
issues: write # 允许创建 Issue
jobs:
manual-scan:
runs-on: ubuntu-latest
steps:
- name: 📥 检出代码
uses: actions/checkout@v4
- name: 🐍 设置 Python
uses: actions/setup-python@v5
with:
python-version: '3.10'
- name: 🔍 执行扫描
run: |
python scan_github.py --auto --max-repos ${{ github.event.inputs.max_repos }}
- name: 💾 提交报告到仓库
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git add scan_reports/
git commit -m "📄 添加扫描报告 [skip ci]"
git push
- name: 📤 上传 Artifacts
uses: actions/upload-artifact@v4
with:
name: scan-report-${{ github.run_number }}
path: scan_reports/
retention-days: 90
- name: 📢 创建告警 Issue
if: steps.analyze.outputs.has_findings == 'true'
uses: actions/github-script@v7
with:
script: |
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: '🚨 发现潜在密钥泄露',
body: '详见扫描报告...',
labels: ['security', 'auto-scan']
});
✨ 核心特性
1. 多模式扫描
支持 4 种扫描模式:
# 自动搜索 AI 相关项目
python scan_github.py --auto --max-repos 50
# 扫描指定用户的所有公开仓库
python scan_github.py --user username
# 扫描指定组织
python scan_github.py --org organization
# 扫描单个仓库
python scan_github.py --repo owner/repo_name
2. 智能过滤
- ✅ 自动过滤示例代码(包含
example、demo、placeholder等关键词) - ✅ 跳过二进制文件和媒体文件
- ✅ 排除
node_modules、.git等目录 - ✅ 置信度评分(高/中/低)
3. 扫描历史管理
避免重复扫描,提高效率:
# scan_history.py
class ScanHistory:
def mark_as_scanned(self, repo_name: str, findings_count: int, scan_type: str):
"""标记仓库为已扫描"""
self.history[repo_name] = {
'last_scan': datetime.now().isoformat(),
'findings_count': findings_count,
'scan_type': scan_type
}
self._save_history()
def is_scanned(self, repo_name: str) -> bool:
"""检查仓库是否已扫描"""
return repo_name in self.history
4. 超时保护
GitHub Actions 有 60 分钟的限制,我加入了超时保护:
def _check_timeout(self, current_idx: int, total_repos: int) -> bool:
"""检查是否超时"""
if self._is_timeout():
elapsed_minutes = (time.time() - self.scan_start_time) / 60
print(f"⏰ 扫描超时(已运行 {elapsed_minutes:.1f} 分钟)")
print(f"✅ 已完成 {current_idx}/{total_repos} 个仓库的扫描")
print(f"💾 已保存扫描数据,剩余仓库将在下次扫描时处理")
return True
return False
🎨 项目亮点
1. 完全自动化
- 基于 GitHub Actions,无需本地环境
- 每天自动运行(北京时间 10:00)
- 发现问题自动创建 Issue
2. 双重存储
- 报告提交到仓库
scan_reports/目录永久保存 - 同时上传到 Artifacts(保留 90 天)
3. 智能检测
- 30+ 种 API 密钥检测模式
- 自动过滤示例代码
- 置信度评分减少误报
4. 详细报告
- 包含文件路径、行号、代码片段
- 部分隐藏密钥保护安全
- 统计分析和安全建议
5. 用户友好
- 命令行界面清晰
- 报告格式美观易读
- 完整的中文文档
📊 实际效果
运行一次扫描(50 个仓库):
🚀 开始自动搜索 AI 相关项目
🎯 目标: 找到并扫描 50 个未扫描的仓库
📦 找到 50 个待扫描的仓库
🔍 [1/50] 扫描仓库: user1/ai-chatbot
⚠️ 发现 2 个潜在问题
🔍 [2/50] 扫描仓库: user2/gpt-demo
✅ 未发现明显问题
...
📝 生成报告...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
✅ 扫描完成!
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
📄 报告已保存至: ./scan_reports/scan_report_20251016_142530.txt
⚠️ 发现 8 个潜在安全问题!
🔴 建议立即:
1. 查看详细报告
2. 撤销泄露的 API 密钥
3. 检查是否被滥用
4. 从 Git 历史中删除敏感信息
🛠️ 技术栈
- 语言:Python 3.10+
- 核心库:
PyGithub- GitHub API 交互python-dotenv- 环境变量管理re- 正则表达式匹配
- 自动化:GitHub Actions
- 报告格式:纯文本(易于版本控制)
🎯 适用场景
- 个人开发者:定期检查自己的仓库
- 团队/组织:批量扫描团队成员的项目
- 安全审计:企业级代码安全审查
- 教育用途:学习敏感信息检测技术
💡 开发心得
- 正则表达式设计:平衡准确率和召回率,避免过多误报
- 速率限制处理:GitHub API 有速率限制,需要合理控制请求频率
- 超时保护:GitHub Actions 有时间限制,需要做好进度保存
- 示例过滤:通过关键词识别示例代码,显著降低误报率
- 模块化设计:每个模块职责明确,易于维护和扩展
📝 总结
通过模块化设计和 GitHub Actions 自动化,实现了一个完全免费、全自动运行的安全扫描工具。
希望这个工具能帮助更多开发者避免 API 密钥泄露的风险!如果你也遇到过类似问题,或者有更好的解决方案,欢迎在评论区交流讨论!
觉得有用的话,给个 ⭐ Star 支持一下吧!🙏