如何用GitGuardian检测硬编码的秘密

857 阅读10分钟

开发人员正在以前所未有的速度利用他们从软件生态系统中所需要的东西来构建功能。这些不断扩大的选择包括开源库和包、SaaS工具、部署系统、云服务等等。为了保持安全,我们总是需要一样东西:秘密。

什么是秘密?

秘密是应用、服务或基础设施中使用的数字认证凭证(API密钥、证书和令牌)。就像密码(在MFA的情况下加上设备)用于验证一个人一样,秘密用于验证一个系统以实现互操作性。

为什么秘密在CI/CD环境中是个问题?

软件工程师需要处理越来越多的凭证,因为他们使用CI/CD管道将工件、应用程序和基础设施部署到多个环境。有许多地方的秘密可能会不安全地暴露。

  • 源代码
  • 构建、测试或部署CI/CD工作流程
  • 容器镜像层
  • 运行器控制台输出

泄露的凭证不仅仅是一个安全问题;旋转泄露的秘密会中断CI/CD工作流程。

秘密怎么会出现在源代码中?

两个字:人为错误。

绝大多数泄露的凭证是错误的,并不是出于恶意的。硬编码凭证可能是一个临时的解决方案,有时开发人员没有意识到Git实际上是在跟踪一个被删除的秘密。新的开发者不知道正确的程序,或者跳过了一个测试。可能出现的错误不胜枚举。

为什么硬编码的秘密与其他类型的漏洞不同?

与其他漏洞不同的是,其他漏洞是针对代码中的特定弱点,而检测秘密则需要一个项目的整个代码库历史。

当一个开发者错误地提交了一个秘密时,有两种可能性:要么承认这个情况,要么不承认。

在前一种情况下,一个非常常见的错误是删除它,并简单地提交修改。这个秘密从源代码的当前状态中消失了,但它仍然在提交历史中!在后一种情况下,很可能是删除了这个秘密,然后简单地提交了修改。

在后一种情况下,这个秘密很可能会到达远程版本控制系统(VCS)。这时,该秘密就已经被认为是泄露了(最好的情况是,在代码审查阶段就被发现,但此时可能已经需要对该秘密进行轮换)。

发现隐藏在代码库历史深处的有效秘密是很常见的。秘密检测需要考虑到这一攻击面*,并*扫描存储库的增量变化,以防止这类泄漏。

开始使用GitGuardian

在本教程中,您将学习如何将GitGuardian实时监控添加到CircleCI工作流程中,以扫描每个新提交的秘密。

GitGuardian可以在历史或增量提交中检测仓库中的秘密。秘密检测发生在开发生命周期的多个阶段:在开发人员的本地机器上的预提交钩子 或预推送钩子,在预接收钩子或CI环境中。

通过GitGuardian仪表盘,在全公司范围内实现了可视性,以确保所有存储库的安全。

Company-wide perimeter

仪表板还能让开发人员和AppSec工程师在整个修复过程中进行协作。我们不会在本教程中介绍这些,但你可以在文档中了解更多。

前提条件

要遵循本教程,你将需要。

  1. 一个CircleCI账户
  2. 一个GitHub账户
  3. 一个GitGuardian账户

分叉样本仓库

在本教程中,你将使用GitGuardian的sample_secrets 测试仓库。这个仓库包含了各种用于测试的秘密。将其分叉到您的GitHub用户账户或您是管理员的GitHub组织。

然后,打开CircleCI项目页面,点击sample_secrets 名称,然后选择Faster。提交一个启动CI管道到一个新的分支

这将在仓库中创建新的分支circleci-project-setup ,包含演示工作流say-hello-workflow ,由./circleci/config.yml 文件配置。

创建一个GitGuardian API令牌

你需要一个GitGuardian API令牌来使用GitGuardian orb。 从GitGuardian仪表板,进入API > 个人访问令牌,然后点击创建令牌。给该令牌一个scan 范围和一个令人难忘的名字。

Create a token

复制该令牌并保存好,这是你唯一可以查看的时间。

注意。 如果你使用的是GitGuardian的商业计划或30天的商业试用,请创建一个服务账户,而不是个人访问令牌。服务账户是一种特殊类型的API密钥,旨在代表一个非人类用户,如CI运行器。要创建一个,请进入API > 服务账户,并遵循相同的步骤。

在CircleCI仪表板上,点击sample_secrets 项目,然后点击项目设置>环境变量。点击添加环境变量。将其命名为GITGUARDIAN_API_KEY ,并赋予它与之前复制的令牌相同的值。

用ggshield扫描增量变化

现在你需要在你的CircleCIconfig.yml 中添加一个工作流来使用ggshield orb。

复制并替换成这个文件。

version: 2.1

orbs:
  ggshield: gitguardian/ggshield@volatile

workflows:
  scan_my_commits:
    jobs:
      - ggshield/scan:
          name: ggshield-scan
          base_revision: <<pipeline.git.base_revision>>
          revision: <<pipeline.git.revision>>

你也可以在ggshield orb注册表页面找到这个片段。

base_revisionrevision 的值将在管道被触发时被填入。

  • base_revision 是第一个要扫描的提交的ID。
  • revision 是要扫描的最后一个提交的ID。

在这个配置中,只扫描最新的提交,这对CI流水线来说很方便。您可能不希望在每次管道启动时都扫描整个git历史。该扫描对上次修订后的所有提交进行操作,以确保没有秘密被提交后删除。

当你完成了config.yml ,提交它,推送它,然后去CircleCI仪表盘上看管道的启动。如果这是你第一次使用第三方轨道,你可能需要在组织设置>安全>轨道安全设置中接受使用第三方轨道。

Scan commits

点击作业,了解到有要扫描的承诺: 1.

#!/bin/bash -eo pipefail

ggshield secret scan -v ci

CIRCLE_RANGE: dea39f827dfe23f06f4ea63d7fb16ab0c363db9d...90220851160dcf018f372536da223dc0396aa247
CIRCLE_SHA1: 90220851160dcf018f372536da223dc0396aa247
Commits to scan: 1
Scanning Commits---------------------------------]    0%Scanning Commits  [####################################]  100%
secrets-engine-version: 2.71.0
No secrets have been found
commit 90220851160dcf018f372536da223dc0396aa247
Author: ***
Date: ***

CircleCI received exit code 0

要验证防护罩是否按预期工作,只需对测试资源库的一个文件提交一个修改。例如,打开sample_secrets/bucket_s3.py 文件,添加或删除尾部的空白,然后提交这个修改(一定要在circle-project-setup 分支上)。

这将会失败,因为ggshield会扫描最新的提交并检测到文件中的两个秘密。

#!/bin/bash -eo pipefail
ggshield secret scan -v ci
CIRCLE_RANGE: 90220851160dcf018f372536da223dc0396aa247...2d08c13226628ecfb3ee9a07001c185915a84adf
CIRCLE_SHA1: 2d08c13226628ecfb3ee9a07001c185915a84adf
Commits to scan: 1
Scanning Commits---------------------------------]    0%Scanning Commits  [####################################]  100%

secrets-engine-version: 2.71.0

commit 2d08c13226628ecfb3ee9a07001c185915a84adf
Author: XXXX
Date: XXXX

🛡️  ⚔️  🛡️  2 incidents have been found in file bucket_s3.py

>>> Incident 1(Secrets detection): AWS Keys (Validity: Invalid)  (Ignore with SHA: 9f2785cab705507aaea637b8b38d8e1ff9ce8a4334dda586187cbb018ed33163) (1 occurrence)
 8  8 |
 9  9 | def aws_upload(data: Dict):
10    |     database = aws_lib.connect("AKIA************WSZ5", "hjshnk5**************************89sjkja") |_____client_id____|
10    |     database = aws_lib.connect("AKIA************WSZ5", "hjshnk5**************************89sjkja")
10 |     database = aws_lib.connect("AKIA************WSZ5", "hjshnk5**************************89sjkjb")
11 11 |     database.push(data)

>>> Incident 2(Secrets detection): AWS Keys (Validity: Invalid)  (Ignore with SHA: e8077f59453457d2b3d980be4d8655eaa901c7aa8810a6079b429477e07a57f9) (1 occurrence)
 9  9 | def aws_upload(data: Dict):
10    |     database = aws_lib.connect("AKIA************WSZ5", "hjshnk5**************************89sjkja")
10 |     database = aws_lib.connect("AKIA************WSZ5", "hjshnk5**************************89sjkjb")
|_____client_id____|
10 |     database = aws_lib.connect("AKIA************WSZ5", "hjshnk5**************************89sjkjb")
|_____________client_secret____________|
11 11 |     database.push(data)

Exited with code exit status 1
CircleCI received exit code 1

Validity: Invalid告诉你两件事。

  1. 秘密可以被检查(这并不总是这样的)。
  2. 该秘密不再有效。

更进一步:扫描提交历史

但如果你想扫描所有过去的提交的秘密呢?历史扫描是由GitGuardian在你分叉sample_secrets 仓库时为你完成的(这是默认行为)。

进入GitGuardian 仪表盘,在Perimeter页面搜索sample_secrets 源。你应该看到,GitGuardian 在该仓库中检测到了九个公开的秘密事件。

Scan commits

如果需要,你可以再次扫描选中的源

Open secrets

点击该源,显示秘密表。在历史扫描中检测到的事件会被标记。

Table of secrets

你可以用命令ggshield scan repo ,扫描任何任意的git历史,但没有专门的兽皮。

更进一步:补救和开发者工作流程

如果你走到了这一步,恭喜你!你可以确定任何秘密提交的文件都不会被发现。你可以确信,任何提交到这个仓库的秘密都会破坏管道,并在仪表板上报告,与所有其他过去的事件一起。

我们给所有GitGuardian用户的建议是:预防永远优于补救,所以要在开发者的工作流程中尽可能早地整合秘密检测。

为了理解这个原因,请想象一下,GitGuardian在CircleCI的工作流程中发现了一个广泛使用的秘密。最好的做法是立即撤销和旋转它,就像它被泄露了一样,即使它没有被泄露。但事实是,轮换一个秘密几乎总是一个艰难的工作。它可能意味着许多人的工作流程被打断。它可能会在整个CI/CD链中,甚至在生产中造成意外的失败。

这就是为什么我们一直提倡将GitGuardian与ggshield集成到开发者的工作流程中,作为预提交、预推送(客户端)或预接收(服务器端)的钩子,确保没有秘密可以首先到达版本控制系统。

你还可以将GitGuardian原生地集成到源码控制管理平台中。

总结

本教程展示了秘密是如何轻易被泄露的。与运行时漏洞不同,泄露的秘密可以在旧的提交中持续存在,是一种真正的威胁。这就是为什么在你的CI工作流程中使用秘密检测器是代码安全的一个必备条件。

这种意识是在安全、运营和开发人员之间建立共同责任文化的重要第一步,以防止生产问题,保持管道运行,并尽快修复问题。