github自动打野,发现 别人的 ai_api_key 密钥

328 阅读8分钟

🔍 开发记录:打造一个自动化的 GitHub AI API 密钥泄露扫描工具

📖 项目背景

最近在使用各种 AI 服务时,发现不少开发者会不小心将 API 密钥(OpenAI、Anthropic Claude 等)提交到 GitHub 上,这可能导致:

  • 🚨 API 密钥被他人盗用
  • 💸 产生巨额费用(有人因此损失几千美元)
  • 🔐 账号安全风险

于是我决定开发一个自动化扫描工具,可以扫描到别人仓库的 api_key。

🎯 项目目标

  1. 自动化:基于 GitHub Actions,无需本地运行
  2. 智能检测:支持多种 AI API 密钥格式,减少误报
  3. 详细报告:生成完整的扫描报告,包含文件路径、行号、置信度评级
  4. 即时告警:发现问题自动创建 Issue 通知
  5. 完全免费:利用 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. 智能过滤

  • ✅ 自动过滤示例代码(包含 exampledemoplaceholder 等关键词)
  • ✅ 跳过二进制文件和媒体文件
  • ✅ 排除 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
  • 报告格式:纯文本(易于版本控制)

🎯 适用场景

  1. 个人开发者:定期检查自己的仓库
  2. 团队/组织:批量扫描团队成员的项目
  3. 安全审计:企业级代码安全审查
  4. 教育用途:学习敏感信息检测技术

💡 开发心得

  1. 正则表达式设计:平衡准确率和召回率,避免过多误报
  2. 速率限制处理:GitHub API 有速率限制,需要合理控制请求频率
  3. 超时保护:GitHub Actions 有时间限制,需要做好进度保存
  4. 示例过滤:通过关键词识别示例代码,显著降低误报率
  5. 模块化设计:每个模块职责明确,易于维护和扩展

📝 总结

通过模块化设计和 GitHub Actions 自动化,实现了一个完全免费、全自动运行的安全扫描工具

希望这个工具能帮助更多开发者避免 API 密钥泄露的风险!如果你也遇到过类似问题,或者有更好的解决方案,欢迎在评论区交流讨论!


项目地址:github.com/gaocaipeng/…

觉得有用的话,给个 ⭐ Star 支持一下吧!🙏