SuperAPI AI 工具集

secrets-management

安全与审计

查看项目

更新于 10-15 08:43

技能介绍

用途

本技能旨在防止 API 密钥、密码和令牌等硬编码密钥泄露到源代码管理中。资料指出,公开仓库中的密钥被视为严重疏忽(gross negligence),这是马来西亚 PDPA 相关法院案例中的发现 #4。

核心规则

绝不将密钥提交到源代码。始终使用环境变量或密钥管理器。

什么算作密钥

资料列出的密钥类型包括:

  • API 密钥(Stripe、OpenAI、AWS 等)
  • 带密码的数据库连接字符串
  • JWT 签名密钥
  • OAuth 客户端密钥
  • Webhook 签名密钥
  • 私钥(RSA、SSH)
  • 服务账号凭据

用于审计的搜索模式

资料提供了以下检测模式:

# Hardcoded strings that look like secrets
(api[_-]?key|secret|password|token|credential|private[_-]?key)\s*[:=]\s*["'][^"']{8,}["']

# AWS keys
AKIA[0-9A-Z]{16}

# Generic high-entropy strings in assignments
(SECRET|KEY|TOKEN|PASSWORD|CREDENTIAL)\s*=\s*["'][A-Za-z0-9+/=]{20,}["']

# Connection strings with embedded passwords
(mongodb|postgres|mysql|redis):\/\/[^:]+:[^@]+@

正确设置方式

1. 创建 .env 文件(绝不提交)

# .env (local development only)
DATABASE_URL=postgres://user:pass@localhost:5432/mydb
STRIPE_SECRET_KEY=sk_test_abc123
JWT_SECRET=your-random-64-char-string
OPENAI_API_KEY=sk-proj-abc123

2. 创建 .env.example(提交,不含真实值)

# .env.example (committed to repo as documentation)
DATABASE_URL=postgres://user:password@localhost:5432/dbname
STRIPE_SECRET_KEY=sk_test_xxx
JWT_SECRET=generate-with-openssl-rand-base64-48
OPENAI_API_KEY=sk-proj-xxx

3. 添加到 .gitignore

# Secrets - NEVER commit these
.env
.env.local
.env.production
.env.*.local
*.pem
*.key
service-account.json
credentials.json

各框架特定模式

Node.js / TypeScript

错误写法:

const stripe = new Stripe('sk_live_abc123xyz');
const jwt = sign(payload, 'my-super-secret-key');
const db = new Pool({ password: 'production-password-123' });

正确写法:

import { env } from 'process';

const stripe = new Stripe(env.STRIPE_SECRET_KEY!);
const jwt = sign(payload, env.JWT_SECRET!);
const db = new Pool({ connectionString: env.DATABASE_URL });

// Validate at startup
const required = ['STRIPE_SECRET_KEY', 'JWT_SECRET', 'DATABASE_URL'];
for (const key of required) {
  if (!env[key]) throw new Error(`Missing required env var: ${key}`);
}

Cloudflare Workers

错误写法:

const API_KEY = 'sk-abc123';

export default {
  async fetch(request: Request) {
    return fetch('https://api.openai.com/v1/chat', {
      headers: { Authorization: `Bearer ${API_KEY}` }
    });
  }
};

正确写法:

// wrangler.toml - declare bindings
// [vars]
// PUBLIC_URL = "https://myapp.com"
//
// Secrets added via: npx wrangler secret put OPENAI_API_KEY

interface Env {
  OPENAI_API_KEY: string;
  PUBLIC_URL: string;
}

export default {
  async fetch(request: Request, env: Env) {
    return fetch('https://api.openai.com/v1/chat', {
      headers: { Authorization: `Bearer ${env.OPENAI_API_KEY}` }
    });
  }
};

Laravel / PHP

错误写法:

'stripe' => [
    'secret' => 'sk_live_abc123xyz',
],

正确写法:

// config/services.php
'stripe' => [
    'secret' => env('STRIPE_SECRET_KEY'),
],

// Validate in AppServiceProvider::boot()
$required = ['STRIPE_SECRET_KEY', 'DB_PASSWORD', 'APP_KEY'];
foreach ($required as $key) {
    if (empty(env($key))) {
        throw new \RuntimeException("Missing env: {$key}");
    }
}

Python

错误写法:

OPENAI_API_KEY = "sk-proj-abc123"
DATABASE_URL = "postgres://admin:password123@prod-db:5432/app"

正确写法:

import os
from dotenv import load_dotenv

load_dotenv()

OPENAI_API_KEY = os.environ["OPENAI_API_KEY"]
DATABASE_URL = os.environ["DATABASE_URL"]

# Pydantic settings (recommended)
from pydantic_settings import BaseSettings

class Settings(BaseSettings):
    openai_api_key: str
    database_url: str
    jwt_secret: str

    class Config:
        env_file = ".env"

settings = Settings()

密钥轮换策略

何时轮换

  • 密钥曾被提交到 git 时立即轮换(即使后来被移除)
  • 团队成员离开时立即轮换
  • 生产密钥每 90 天轮换一次(最佳实践)
  • 任何安全事件之后

如何在不中断服务的情况下轮换

// Support both old and new key during rotation window
const VALID_API_KEYS = [
  env.API_KEY_CURRENT,
  env.API_KEY_PREVIOUS, // Remove after 24h
].filter(Boolean);

function validateApiKey(key: string): boolean {
  return VALID_API_KEYS.includes(key);
}

紧急情况:密钥已被提交

如果密钥被推送到 git,即使只是短暂地:

  1. 立即轮换密钥——从提供者处生成新密钥
  2. 撤销旧密钥——不要只是轮换,要明确撤销
  3. 检查未授权使用——查看访问日志
  4. 清理 git 历史(可选,密钥已经泄露):
# Only if repo is private and you caught it fast
git filter-branch --force --index-filter \
  "git rm --cached --ignore-unmatch .env" \
  --prune-empty --tag-name-filter cat -- --all
  1. 添加到 .gitignore 以防止再次发生

CI/CD 密钥注入

GitHub Actions

# Use repository secrets, never hardcode
env:
  DATABASE_URL: ${{ secrets.DATABASE_URL }}
  STRIPE_SECRET_KEY: ${{ secrets.STRIPE_SECRET_KEY }}

Cloudflare Workers (Wrangler)

# Set secrets via CLI (never in wrangler.toml)
npx wrangler secret put DATABASE_URL
npx wrangler secret put STRIPE_SECRET_KEY

PDPA 2024 后果

资料指出,根据 2010 年 PDPA 法案第 5(1) 和 5(2) 条(2024 年修订):

  • 罚款最高 RM1,000,000
  • 监禁最长 3 年

公开仓库中的硬编码密钥被视为严重疏忽(kecuaian melampau)。如果这些密钥提供对个人数据的访问,开发者将承担刑事责任。