mcpField
S
sast-hardcodedsecrets

sast-hardcodedsecrets

github.com/utkusen安全1.3k66 次安装5 天前更新
代码仓库

关于 sast-hardcodedsecrets

用于查找公开可访问代码中硬编码机密的安全审查 skill。它区分客户端可访问代码与仅后端位置,再分批验证候选项并汇总结果;运行前需要 `sast/architecture.md`。

主要功能

  1. 01发现机密候选项
  2. 02评估公开代码中的暴露
  3. 03分批验证候选项
  4. 04汇总分析结果报告

适用场景

  1. 01检查前端构建产物中的凭据
  2. 02审查移动端代码中的嵌入机密
  3. 03区分客户端暴露项与仅后端值

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/utkusen/sast-skills --skill sast-hardcodedsecrets

安装后在 Claude Code 中输入/sast-hardcodedsecrets确认已加载。

详细介绍

公开代码中的硬编码密钥检测

你正在进行一项专项安全评估,以查找公开可访问代码中暴露的硬编码敏感数据。此技能采用包含子 Agent 的三阶段方法:侦察(查找所有潜在的密钥候选项)、分批验证(以每批 3 个的并行批次确认每个候选项是否为公开可访问代码中的真实密钥),以及合并(将各批次报告整合到一个文件中)。

前提条件:必须存在 sast/architecture.md。如果不存在,请先运行分析技能。


什么是公开代码中的硬编码密钥

硬编码密钥是直接以字符串字面量形式嵌入源代码中的敏感凭据,例如 API 密钥、访问令牌、私钥、密码、签名密钥和数据库连接字符串。

此技能专门关注最终出现在公开可访问代码中的密钥,也就是说,攻击者无需任何服务器端访问权限即可提取这些密钥。硬编码在后端服务器代码中的密钥属于不良实践,但外部攻击者无法通过检查已部署的应用直接利用它。硬编码在前端 JavaScript 或移动应用二进制文件中的密钥则可以被直接提取。

核心问题:外部攻击者能否在不访问服务器的情况下,从已部署的应用中获取此密钥?

应报告的内容(公开可访问代码)

部署后,攻击者可以访问以下代码路径:

  • 前端 JavaScript/TypeScript — 在浏览器中运行的任何 .js、.ts、.jsx、.tsx 文件。这包括:
    • React、Angular、Vue、Svelte 组件和页面
    • Next.js 客户端组件(含有 "use client" 的文件,或 app/ 下不含 "use server" 的文件)
    • Nuxt.js 页面和客户端插件
    • public/、static/ 或 assets/ 目录中的原生 JS
    • Webpack/Vite/Rollup 入口文件及其导入的模块
    • 客户端入口文件导入的任何文件(即使它位于 utils/ 或 lib/ 文件夹中)
  • 移动应用代码 — 可通过逆向工程提取(反编译 APK、检查 IPA):
    • Android:Java/Kotlin 源文件
    • iOS:Swift/Objective-C 源文件
    • React Native:JavaScript 包
    • Flutter:Dart 源文件
    • Xamarin:C# 源文件
  • 提供给客户端的 HTML 文件和模板 — 内联 <script> 块、data- 属性、meta 标签
  • 客户端配置文件 — public/、static/、assets/、www/ 目录中的文件
  • Electron/桌面应用源代码 — 可从 ASAR 归档中提取
  • WebAssembly 源代码/配套 JS — JS 胶水代码中的密钥,或可从 WASM 中提取的密钥
不应报告的内容(仅限后端的代码)

请勿标记以下位置中的密钥 — 它们无法公开访问:

  • 服务端应用代码 — Express 路由处理程序(仅服务端)、Django 视图、Flask 路由、Spring 控制器、Rails 控制器、Go HTTP 处理程序、PHP 控制器 — 仅在服务器上运行的代码
  • 服务端 API 路由文件 — Next.js app/api/ 路由、Nuxt 服务端路由、SvelteKit +server.ts 文件
  • 环境文件 — .env、.env.local、.env.production(除非以静态方式提供)
  • 服务端配置 — config/database.yml、settings.py、application.properties、appsettings.json
  • CI/CD 流水线文件 — .github/workflows/、Jenkinsfile、.gitlab-ci.yml
  • Docker/基础设施文件 — Dockerfile、docker-compose.yml、Kubernetes 清单
  • 后端工具/服务文件 — 仅由服务端代码导入的文件
  • 测试文件 — 测试样例和测试配置(除非测试文件会随应用一起发送给客户端)
  • 迁移文件 — 数据库迁移
区分前端与后端

这一点至关重要,需要了解项目架构:

Next.js:app/ 下带有 "use client" 指令或未带有 "use server" 的文件属于客户端组件。app/api/ 下的文件仅在服务端运行。pages/api/ 下的文件仅在服务端运行。pages/ 下非 api 的文件会在服务端和客户端渲染——其中的机密信息会暴露。next.config.js 仅在服务端运行,但 NEXT_PUBLIC_* 环境变量会嵌入客户端代码包。

Nuxt.js:pages/、components/、composables/ 下的文件可由客户端访问。server/ 下的文件仅在服务端运行。

React (CRA/Vite):src/ 中的所有内容都会打包到客户端。REACT_APP_* 和 VITE_* 环境变量会嵌入客户端构建产物。

Angular:src/ 中的所有内容都会打包到客户端。

Vue (Vite):src/ 中的所有内容都会打包到客户端。VITE_* 环境变量会嵌入客户端。

Express/Fastify/Koa:所有内容都在服务端运行,除非从 public/ 或 static/ 目录提供静态文件。

Django/Flask:Python 代码在服务端运行。模板在服务端渲染(模板上下文中的机密信息不会传到客户端,除非明确渲染进 JS)。static/ 中的静态文件可由客户端访问。

Rails:Ruby 代码在服务端运行。app/assets/javascripts/ 或 app/javascript/ 中的资源可由客户端访问。

移动应用:所有源代码都应视为可通过逆向工程公开获取。


要查找的机密信息类型

高置信度模式(可通过正则表达式识别)

这些模式具有明显特征,因此可以高置信度地识别:

机密类型模式
AWS Access Key IDAKIA[0-9A-Z]{16}
AWS Secret Access Key靠近 AKIA 密钥的 40 字符 base64 字符串
Google API KeyAIza[0-9A-Za-z\\-_]{35}
Google OAuth Client SecretGOCSPX-[0-9A-Za-z\\-_]{28}
GitHub Personal Access Tokenghp_[0-9A-Za-z]{36}, github_pat_[0-9A-Za-z_]{82}
GitHub OAuth App Secretgho_[0-9A-Za-z]{36}
GitLab Personal Access Tokenglpat-[0-9A-Za-z\\-_]{20}
Slack Bot/User Tokenxoxb-[0-9A-Za-z\\-]+, xoxp-[0-9A-Za-z\\-]+
Slack Webhook URLhooks.slack.com/services/T[A-Z0-9]+/B[A-Z0-9]+/[A-Za-z0-9]+
Stripe Secret Keysk_live_[0-9A-Za-z]{24,}
Stripe Publishable Keypk_live_[0-9A-Za-z]{24,}(可公开密钥专为客户端使用而设计——除非与秘密密钥同时出现,否则跳过)
Twilio Account SID + Auth TokenAC[0-9a-f]{32}(SID),附近还有 32 位十六进制认证令牌
SendGrid API KeySG\.[0-9A-Za-z\\-_]{22}\.[0-9A-Za-z\\-_]{43}
Mailgun API Keykey-[0-9a-zA-Z]{32}
Firebase Config配置对象中同时包含 apiKey、authDomain、projectId——仅当其中包含服务端/管理员密钥,而非标准客户端配置时才标记
Private RSA/EC/SSH Key`-----BEGIN (RSA
JWT Secret / Signing Key赋给 JWT_SECRET、jwt_secret、signingKey、HMAC_KEY 等变量的字符串
带密码的数据库连接字符串postgresql://user:pass@、mysql://user:pass@、mongodb://user:pass@、redis://:pass@
通用 API Key 赋值名为 *api_key*、*apiKey*、*API_KEY*、*secret*、*SECRET*、*token*、*TOKEN*、*password*、*PASSWORD* 的变量,被赋值为看起来像真实凭据的字符串字面量
Heroku API KeyHeroku 上下文中的 [0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}
Azure Storage Key赋给存储帐户密钥变量的约 88 字符 base64 字符串
OpenAI API Keysk-[A-Za-z0-9]{48} 或 sk-proj-[A-Za-z0-9\\-_]{100,}
Anthropic API Keysk-ant-[A-Za-z0-9\\-_]{90,}
变量名称模式(需要检查值)

搜索名称符合以下模式的变量/常量,并检查赋值是否看起来像真实凭据:

  • api_key、apiKey、API_KEY、ApiKey
  • secret、SECRET、secret_key、secretKey、SECRET_KEY
  • access_token、accessToken、ACCESS_TOKEN
  • auth_token、authToken、AUTH_TOKEN
  • private_key、privateKey、PRIVATE_KEY
  • password、PASSWORD、passwd、PASSWD
  • client_secret、clientSecret、CLIENT_SECRET
  • signing_key、signingKey、SIGNING_KEY
  • encryption_key、encryptionKey、ENCRYPTION_KEY
  • bearer_token、BEARER_TOKEN
  • credentials、CREDENTIALS
  • connection_string、connectionString、DATABASE_URL
哪些不是真正的密钥(可忽略的误报)
  • 占位值:"your-api-key-here"、"TODO"、"xxx"、"changeme"、"REPLACE_ME"、"INSERT_KEY"、"<api_key>"、"dummy"、"test"、"example"、"sample"、"placeholder"
  • 空字符串:""、''
  • 环境变量引用:process.env.API_KEY、os.environ["SECRET"]、ENV["KEY"]——这些会在运行时从环境中读取,而非硬编码
  • 公钥:公钥(而非私钥)就是用于共享的,不属于密钥
  • 可发布/公开 API 密钥:Stripe pk_test_*、pk_live_*;Firebase 客户端配置 apiKey(专为客户端使用而设计);Google Maps 客户端密钥(受 HTTP 引荐来源限制)
  • 测试/开发密钥:sk_test_*(Stripe 测试密钥)、明确命名为测试夹具的文件中的密钥
  • 类型定义/接口:TypeScript interface Config { apiKey: string }——不包含实际值
  • 文档字符串:说明密钥形式的注释
  • 哈希值:不属于密钥的 SHA256/MD5 哈希(例如内容哈希、校验和)
  • 构建时常量:版本字符串、构建 ID、提交哈希

执行

此技能分三个阶段使用子 Agent 运行。将 sast/architecture.md 的内容作为上下文传递给所有子 Agent。

阶段 1:侦察 — 查找潜在机密

启动一个子 Agent,并提供以下指示:

目标:查找代码库中所有以字符串字面量形式出现的硬编码机密(API 密钥、访问令牌、私钥、密码、签名机密、连接字符串)。将结果写入 sast/hardcodedsecrets-recon.md。

背景:你将获得项目架构摘要。利用它了解技术栈、项目结构,以及哪些文件属于前端或后端。

搜索内容:

扫描整个代码库。在此阶段,无论潜在机密位于前端还是后端代码中,都要全部标记——筛选将在阶段 2 进行。

  1. 高置信度正则表达式模式——搜索以下特征鲜明的格式:
    • AWS 密钥:AKIA[0-9A-Z]{16}
    • Google API 密钥:AIza[0-9A-Za-z\-_]{35}
    • GitHub 令牌:ghp_、github_pat_、gho_、ghs_
    • Slack 令牌:xoxb-、xoxp-、xoxa-、xoxr-
    • Stripe 机密密钥:sk_live_、sk_test_
    • SendGrid 密钥:SG\.
    • OpenAI 密钥:sk- 后跟 48 个或更多字母数字字符
    • Anthropic 密钥:sk-ant-
    • 私钥标头:-----BEGIN.*PRIVATE KEY-----
    • 包含内嵌密码的连接字符串:://[^:]+:[^@]+@
  2. 变量赋值模式——搜索名称与机密相关、且被赋予字符串字面量值的变量:
    • 搜索如下模式:apiKey = "..."、api_key = '...'、API_KEY: "..."、secret: "..."、token = "..."、password = "..."、client_secret = "..."
    • 包含所有大小写命名惯例:camelCase、snake_case、SCREAMING_SNAKE_CASE、PascalCase
    • 检查 JS/TS 对象、JSON 文件、YAML/TOML 配置、Python 字典以及类似环境变量的配置
  3. 符合已知密钥格式的内嵌字符串字面量:
    • 赋给身份验证相关变量的长随机字母数字字符串(32 个或更多字符)
    • 身份验证上下文中的 Base64 编码字符串
    • 用作密钥或机密的十六进制字符串(64 个或更多字符)
    • 用作 API 密钥或机密的 UUID

侦察时跳过以下内容:

  • 环境变量读取:process.env.*、os.environ[*]、ENV[*]、System.getenv(*)——这些不是硬编码值
  • 没有值的类型定义:apiKey: string、type Config = { secret: string }
  • 明显的占位符:"your-key-here"、"TODO"、"xxx"、"changeme"、"REPLACE_ME"、"<api_key>"、"dummy"、"test-key"、"example"、"sample"、空字符串
  • 仅描述或记录机密的注释
  • 公钥(非私有的加密密钥)
  • 用作校验和或内容标识符的哈希值
  • .git/、node_modules/、vendor/、venv/、__pycache__/、dist/、build/ 目录中的文件

输出格式——写入 sast/hardcodedsecrets-recon.md:

# 硬编码机密侦察:[项目名称]

## 摘要
找到 [N] 个潜在的硬编码机密候选项。

## 候选项

### 1. [描述性名称,例如“API 配置中的 AWS 访问密钥”]
- **文件**:`path/to/file.ext`(第 X-Y 行)
- **机密类型**:[AWS 密钥 / Google API 密钥 / 通用 API 密钥 / 私钥 / 密码 / JWT 机密 / 连接字符串 / 等]
- **变量/上下文**:[机密出现处的变量名或上下文]
- **检测方法**:[正则匹配 / 变量名称模式 / 内嵌字面量]
- **代码片段**:

[显示包含机密的行——对实际值的中间部分进行脱敏,例如:"AKIA****EXAMPLE" 或 "sk_live_****abcd"]


[对每个候选项重复以上内容]
阶段 1 之后:继续之前检查候选项

阶段 1 完成后,读取 sast/hardcodedsecrets-recon.md。如果侦察结果中没有候选项(摘要显示“Found 0”,或者“Candidates”部分为空或不存在),则完全跳过阶段 2 和阶段 3。改为将以下内容写入 sast/hardcodedsecrets-results.md,然后停止:

# Hardcoded Secrets Analysis Results

No vulnerabilities found.

只有阶段 1 找到至少一个候选项时,才继续阶段 2。

阶段 2:验证——确认公开代码中的真实密钥(分批处理)

阶段 1 完成后,读取 sast/hardcodedsecrets-recon.md,并将候选项拆分为每批最多 3 个候选项。并行启动每批一个子 Agent。每个子 Agent 只验证分配给自己的候选项,并将结果写入自己的批次文件。

分批流程(由你(编排者)执行,而不是子 Agent):

  1. 读取 sast/hardcodedsecrets-recon.md,统计编号候选项部分(### 1.、### 2. 等)的数量。
  2. 将它们拆分为每批最多 3 个候选项。例如,8 个候选项 -> 3 批(1-3、4-6、7-8)。
  3. 从侦察文件中提取每批候选项部分的完整文本。
  4. 并行启动所有批次子 Agent,并仅向每个子 Agent 提供分配给它的候选项。
  5. 每个子 Agent 将结果写入 sast/hardcodedsecrets-batch-N.md,其中 N 是从 1 开始的批次编号。

向每个批次子 Agent 提供以下说明(替换为该批次对应的值):

目标:验证以下硬编码密钥候选项。对每一项,确定 (1) 是否为真实密钥,以及 (2) 是否位于可公开访问的代码中。将结果写入 sast/hardcodedsecrets-batch-[N].md。

分配给你的候选项(来自侦察阶段):

[在此粘贴分配给你的候选项章节的完整文本,并保留原始编号]

背景:你将获得项目架构摘要。请利用它了解技术栈、前后端分离情况、构建流程,以及哪些目录包含客户端代码和服务器端代码。

对于每个候选项,请回答两个问题:

问题 1:这是真实密钥吗?

检查该值是否为真实凭据,而非误报:

  • 该字符串是否具有真实密钥/令牌的熵和格式?(真实 API 密钥通常包含 20 个以上的随机字符)
  • 它是否为已知占位符或示例值?(“your-key-here”、“changeme”、“test”、“example”、“TODO”、“xxx”、“REPLACE_ME”等)
  • 它是否为测试/开发密钥?(Stripe sk_test_*、沙盒凭据、测试夹具中的密钥)
  • 它是否为按设计公开/可发布的密钥?(Stripe pk_live_*、Firebase 客户端 apiKey、Google Maps 浏览器密钥)
  • 它是否实际上是被误识别的环境变量引用?
  • 它是否为哈希、校验和或非密钥标识符?

如果该值不是真实密钥,请将其归类为 Not Vulnerable 并说明原因。

问题 2:它是否位于可公开访问的代码中?

判断外部攻击者能否从已部署的应用中提取该密钥:

可公开访问(报告这些项):

  • 在浏览器中运行的前端 JavaScript/TypeScript(React、Angular、Vue、Svelte 组件/页面)
  • Next.js 客户端组件(包含 "use client" 的文件或客户端渲染的页面)
  • Nuxt.js pages/、components/、客户端 plugins/
  • 任何由客户端入口点导入的 .js/.ts 文件(追踪导入链)
  • 直接提供服务的 public/、static/、assets/、www/ 目录中的文件
  • 包含内嵌 <script> 块的 HTML 文件
  • 移动应用源代码——Android(Java/Kotlin)、iOS(Swift/Objective-C)、React Native JS、Flutter Dart、Xamarin C#——所有移动端代码都可通过逆向工程提取
  • Electron 应用源代码(可从 ASAR 中提取)
  • 内嵌在 JavaScript 中的客户端配置对象(例如 Firebase 配置、分析工具初始化代码)

不可公开访问(不要报告):

  • 服务器端路由处理程序(Express、Django、Flask、Rails、Spring、Go、PHP 控制器)
  • 服务器端 API 路由(Next.js app/api/、Nuxt server/、SvelteKit +server.ts)
  • 仅由服务器端代码导入的后端服务、中间件、实用工具
  • .env 文件、服务器配置文件、Docker/CI 文件
  • 不会发送给客户端的测试文件和夹具
  • 数据库迁移
  • 构建脚本和工具

如何判断文件是否为客户端代码:

  1. 检查文件路径——它是否位于客户端目录下?(CRA/Vite React 中的 src/、Next.js 中的 pages/、Angular 中的 app/、Vue 中的 src/)
  2. 追踪导入链——该文件是否由客户端入口点直接或间接导入?
  3. 检查仅限服务器端的标记——"use server" 指令,或位于 api/、server/ 目录中的文件
  4. 查看 sast/architecture.md,了解项目的前后端分离模式
  5. 对于有歧义的情况(例如共享实用工具文件),请谨慎处理——如果该文件可能被打包到客户端,就视为可公开访问

如果密钥不在可公开访问的代码中,请将其归类为 Not Vulnerable 并说明原因(例如:“仅限服务器端——Express 路由处理程序”)。

分类:

  • Vulnerable:确认是真实密钥,且确认位于可公开访问的代码中。攻击者可以从已部署的应用中提取该密钥。
  • Likely Vulnerable:看起来是真实密钥,且文件可能可由客户端访问,但无法完全确认一项或两项条件(例如导入链有歧义,或无法确定该值是否为生产环境密钥)。
  • Not Vulnerable:要么不是真实密钥(占位符、测试密钥、公开密钥),要么不在可公开访问的代码中(仅限后端)。
  • Needs Manual Review:无法确定该值是否为真实密钥,或文件是否会到达客户端——需要人工判断。

输出格式——写入 sast/hardcodedsecrets-batch-[N].md:

# 硬编码密钥批次 [N] 结果

## 发现项

### [VULNERABLE] 描述性名称
- **文件**:`path/to/file.ext`(第 X-Y 行)
- **密钥类型**:[AWS 密钥 / Google API 密钥 / 等]
- **暴露路径**:[攻击者如何提取——例如“通过 Webpack 打包到客户端 JS,可在浏览器 DevTools 的 Sources 标签页中查看”或“嵌入 Android APK,可通过 `apktool d app.apk` 提取”]
- **问题**:[清晰描述——例如“硬编码在 React 组件中的 AWS 访问密钥会被打包到浏览器端”]
- **影响**:[攻击者利用该密钥可以做什么——例如“完全访问 AWS S3 存储桶,可能导致数据外泄”、“代表组织通过 SendGrid 发送邮件”、“通过 API 访问用户数据”]
- **证据**:

[部分遮蔽密钥值的代码片段]

- **修复建议**:[将密钥移至服务器端环境变量。如果客户端需要调用此 API,请通过后端代理。对于移动应用,请使用后端代理或 OAuth 流程,而不是嵌入密钥。]
- **验证步骤**:

[如何确认此发现:

  • 对于 Web 应用:“打开浏览器 DevTools > Sources > 在打包后的 JS 文件中搜索 ‘AKIA’”
  • 对于移动应用:“运行 apktool d app.apk 并 grep 搜索密钥模式”
  • 对于 Electron:“提取 ASAR 归档并搜索密钥”]

### [LIKELY VULNERABLE] 描述性名称
- **文件**:`path/to/file.ext`(第 X-Y 行)
- **密钥类型**:[类型]
- **暴露路径**:[推测该密钥如何到达客户端]
- **问题**:[不确定之处]
- **风险**:[为什么仍然存在风险]
- **证据**:

[代码片段]

- **修复建议**:[修复建议]

### [NOT VULNERABLE] 描述性名称
- **文件**:`path/to/file.ext`(第 X-Y 行)
- **原因**:[例如:“占位符——‘your-api-key-here’”或“仅限服务器端——Django 视图,不会到达客户端”或“Stripe 可发布密钥——专为客户端使用而设计”]

### [NEEDS MANUAL REVIEW] 描述性名称
- **文件**:`path/to/file.ext`(第 X-Y 行)
- **不确定性**:[自动化分析无法确定状态的原因]
- **建议**:[需要人工检查的内容]
阶段 3:合并——汇总批次结果

在所有阶段 2 批次子 Agent 完成后,读取每个 sast/hardcodedsecrets-batch-*.md 文件,并将它们合并为一个 sast/hardcodedsecrets-results.md。由你(编排者)直接执行此操作——无需子 Agent。

合并流程:

  1. 读取所有 sast/hardcodedsecrets-batch-1.md、sast/hardcodedsecrets-batch-2.md……文件。
  2. 收集每个批次文件中的所有发现,并合并为一个列表,同时保留原始分类和所有详细字段。
  3. 汇总所有批次的总数,用于执行摘要。
  4. 按以下格式将合并后的报告写入 sast/hardcodedsecrets-results.md:
# Hardcoded Secrets Analysis Results: [Project Name]

## Executive Summary
- Candidates analyzed: [total across all batches]
- Vulnerable: [N]
- Likely Vulnerable: [N]
- Not Vulnerable: [N]
- Needs Manual Review: [N]

## Findings

[All findings from all batches, grouped by classification:
 VULNERABLE first, then LIKELY VULNERABLE, then NEEDS MANUAL REVIEW, then NOT VULNERABLE.
 Preserve every field from the batch results exactly as written.]
  1. 写入 sast/hardcodedsecrets-results.md 后,删除所有中间批次文件(sast/hardcodedsecrets-batch-*.md)。

重要提醒

  • 读取 sast/architecture.md,并将其内容作为上下文传递给所有子 Agent。
  • 阶段 2 必须在阶段 1 完成后运行——它依赖侦察结果。
  • 阶段 3 必须在所有阶段 2 批次完成后运行——它依赖所有批次的输出。
  • 每个子 Agent 处理 3 个候选项。如果候选项总数为 1-3 个,则使用单个子 Agent。如果有 10 个,则使用 4 个子 Agent(3+3+3+1)。
  • 并行启动所有批次子 Agent——不要按顺序运行。
  • 每个批次子 Agent 只接收侦察文件中分配给它的候选项文本,而不是整个侦察文件。这样可以让每个子 Agent 的上下文保持精简、聚焦。
  • 关键区别在于是否可公开访问:Django 视图中硬编码的 AWS 密钥是不良实践,但对于此技能来说并不构成发现(它位于服务端)。同一个密钥如果出现在 React 组件中,则构成发现,因为它会随代码发送到浏览器。
  • 不确定时,追踪导入链:src/utils/config.ts 中的文件可能同时被服务端和客户端代码导入。检查它被哪些代码导入。如果任何客户端代码路径导入了它,密钥就会暴露。
  • 移动应用始终属于公开内容:Android、iOS、React Native、Flutter 和 Xamarin 应用中的所有源代码都应视为可提取。APK 可以用 apktool/jadx 反编译,IPA 可以检查,React Native 中的 JS bundle 是明文。
  • Firebase 客户端配置通常不是密钥:标准 Firebase 客户端配置(apiKey、authDomain、projectId 等)是为客户端使用而设计的,并由 Firebase 安全规则保护。仅标记 Firebase 管理员/服务账号密钥或服务器密钥(例如 FIREBASE_ADMIN_SDK,或包含 private_key 的服务账号 JSON)。
  • Stripe 可发布密钥不是密钥:pk_live_* 和 pk_test_* 是为客户端使用而设计的。仅标记 sk_live_* 和 sk_test_*(秘密密钥)。
  • NEXT_PUBLIC_*、REACT_APP_*、VITE_* 环境变量:这些变量会在构建时嵌入客户端 bundle。如果代码引用 process.env.NEXT_PUBLIC_API_KEY,则该值可由客户端访问——但实际硬编码值通常位于 .env 文件中,该文件通常会被 gitignore 忽略。只有在源代码中硬编码了实际密钥值时才标记;仅从环境变量读取时不要标记。
  • 在输出中遮盖密钥:展示代码片段时,始终对密钥值进行部分遮盖(例如 AKIA****WXYZ、sk_live_****abcd)。切勿在结果文件中写入完整密钥值。
  • 对公开可访问性存疑时,将其分类为“需要人工审查”,而不是“无漏洞”。在安全评估中,漏报比误报更糟。
  • 清理中间文件:写入最终的 sast/hardcodedsecrets-results.md 后,删除 sast/hardcodedsecrets-recon.md 和所有 sast/hardcodedsecrets-batch-*.md 文件。

评论

支持 Markdown