在编写工作流和使用 GitHub Actions 安全功能时,查找有关安全最佳做法的信息。
写入工作流
对敏感信息使用机密
但是,由于机密值可以通过多种方式转换,因此不能保证自动修订。 请遵循以下最佳做法来限制与机密相关的风险。
- 最低权限原则
- 对存储库具有写入访问权限的任何用户都有存储库中配置的所有机密的读取访问权限。 因此,应确保工作流中使用的凭据具有所需的最低权限。
- Actions 可以从
github.token
上下文访问GITHUB_TOKEN
来使用它。 有关详细信息,请参阅“上下文参考”。 因此,应确保向GITHUB_TOKEN
授予所需的最低权限。 将GITHUB_TOKEN
的默认权限设置为只读取存储库内容是良好的安全做法。 然后可以根据需要增加工作流程文件中个别任务的权限。 有关详细信息,请参阅“在工作流程中使用 GITHUB_TOKEN”。
- 屏蔽敏感数据
- 敏感数据绝不能以明文存储在工作流文件中****。 使用
::add-mask::VALUE
屏蔽所有非 GitHub 机密的敏感信息。 这会导致值被视为机密并从日志中修订。 有关屏蔽数据的详细信息,请参阅“GitHub Actions 的工作流命令”。
- 敏感数据绝不能以明文存储在工作流文件中****。 使用
- 删除和轮换公开的机密
- 工作流运行器执行机密的修订。 这意味着只有在作业中使用并且运行器可以访问的情况下,才会对机密进行修订。 如果将未修订的机密发送到工作流运行日志,则应删除日志并轮换机密。 有关删除日志的信息,请参阅“Using workflow run logs”。
- 切勿将结构化数据用作机密
- 结构化数据可能导致日志中的密码编校失败,因为编校很大程度上取决于查找特定密码值的完全匹配项。 例如,不要使用 JSON、XML 或 YAML(或类似)的 Blob 来封装密码值,否则会显著降低密码被正确编校的可能性。 而应为每个敏感值创建单独的密码。
- 注册工作流中使用的所有机密
- 如果机密用于生成工作流中的另一敏感值,则该生成值应正式注册为机密,以便在它出现在日志中时对其进行编辑。 例如,如果使用私钥生成签名的 JWT 来访问 Web API,请确保将该 JWT 注册为密码,否则,如果它进入日志输出,则不会得到编校。
- 注册密码也适用于任何类型的转换/编码。 如果以某种方式(如 Base64 或 URL 编码)转换您的密码,请确保将新值也注册为密码。
- 审核机密的处理方式
- 审核密码的使用方式,以帮助确保按预期方式处理密码。 您可以通过检查执行工作流程的仓库的源代码并检查工作流程中使用的任何操作来进行审核。 例如,确认它们未发送到非预期主机,或明确打印到日志输出。
- 在测试有效/无效输入后查看工作流程的运行日志,并确认密码已正确编校或未显示。 调用的命令或工具向
STDOUT
和STDERR
发送错误的方式并不总是很明显,并且机密随后可能会出现在错误日志中。 因此,在测试有效和无效的输入后,最好是手动查看工作流程日志。 有关如何清理可能无意中包含敏感数据的工作流日志的信息,请参阅“Using workflow run logs”。
- 审核并轮换已注册机密
- 定期查查已注册的密码,以确认它们仍是必需的。 删除不再需要的密码。
- 定期轮换密码,以减小泄露的密码有效的时间窗。
- 考虑要求对访问机密进行审查
- 您可以使用所需的审查者来保护环境机密。 在审查者批准之前,工作流程作业无法访问环境机密。 有关在环境中存储机密或需要审查环境的详细信息,请参阅“在 GitHub Actions 中使用机密”和“管理部署环境”。
减少脚本注入攻击的良好做法
缓解工作流中脚本注入风险的推荐方法:
使用操作而不是内联脚本
建议的方法是创建一个 JavaScript 操作,将上下文值作为参数处理。 此方法不易受到注入攻击,因为上下文值不用于生成 shell 脚本,而是作为参数传递给该操作:
uses: fakeaction/checktitle@v3
with:
title: ${{ github.event.pull_request.title }}
使用中间环境变量
对于内联脚本,处理不信任输入的首选方法是将表达式的值设置为中间环境变量。 以下示例使用 Bash 将 github.event.pull_request.title
值处理为环境变量:
- name: Check PR title
env:
TITLE: ${{ github.event.pull_request.title }}
run: |
if [[ "$TITLE" =~ ^octocat ]]; then
echo "PR title starts with 'octocat'"
exit 0
else
echo "PR title did not start with 'octocat'"
exit 1
fi
在此示例中,尝试的脚本注入失败,日志中的以下行反映了这一点:
env:
TITLE: a"; ls $GITHUB_WORKSPACE"
PR title did not start with 'octocat'
使用此方法,${{ github.event.pull_request.title }}
表达式的值存储在内存中用作变量,并且不与脚本生成过程交互。 此外,考虑使用双引号 shell 变量来避免单词拆分,但这是写入 shell 脚本的众多一般建议之一,并且不是专门针对 GitHub Actions 的。
限制令牌权限
为了帮助降低暴露令牌的风险,请考虑限制分配的权限。 有关详细信息,请参阅“在工作流程中使用 GITHUB_TOKEN”。
使用第三方操作
工作流程中的个别作业可以与其他作业相互作用(和妥协)。 例如,查询以后作业使用的环境变量,将文件写入以后作业处理的共享目录,或者更直接地与 Docker 套接字接交互,以及检查其他正在运行的容器并执行其中的命令。
这意味着工作流中单一操作的泄露可能很严重,因为这个泄露的操作可以访问存储库中配置的所有机密,并且可以使用 GITHUB_TOKEN
写入存储库。 因此,从 GitHub 上的第三方仓库获取操作的风险很大。 若要了解攻击者可能采取的某些步骤,请参阅“安全使用参考”。
您可以遵循以下良好做法来帮助降低此风险:
-
将操作固定到全长提交 SHA
将操作固定到全长提交 SHA 是当前将操作用作不可变版本的唯一方法。 固定到特定 SHA 有助于降低恶意执行者向操作仓库添加后门的风险,因为他们需要为有效的 Git 对象负载生成 SHA-1 冲突。 选择 SHA 时,应验证它是否来自操作的存储库,而不是存储库分支。
-
审核操作的源代码
确保操作按照预期处理仓库和密码的内容。 例如,确认密码未发送到非预期主机,或者没有被无意中记录。
-
仅当信任创建者时,才将操作固定到标记
尽管固定到提交 SHA 是最安全的选项,但指定标记更方便,而且被广泛使用。 如果要指定标记,请确保信任该操作的创建者。 GitHub Marketplace 上的“已验证创建者”徽章是一个有用的信号,因为它表示该操作是由其身份已被 GitHub 验证的团队编写的。 请注意,即使您信任作者,这种方法也存在风险,因为如果恶意执行者获得对存储操作的仓库的访问权限,便可移动或删除标记。
重新使用第三方工作流程
上述使用第三方操作的相同原则也适用于使用第三方工作流程。 通过遵循上述相同的良好做法,您可以帮助降低与重用工作流程相关的风险。 有关详细信息,请参阅“Reuse workflows”。
GitHub 的安全功能
GitHub 提供了许多功能,使代码更安全。 可以使用 GitHub 的内置功能来了解工作流所依赖的操作,确保收到有关所用操作中的漏洞的通知,或自动执行使工作流中的操作保持最新状态的过程。 如果发布和维护操作,则可以使用 GitHub 与社区就漏洞以及如何修复漏洞进行沟通。 有关 GitHub 提供的安全功能的详细信息,请参阅“GitHub 安全功能”。
使用 CODEOWNERS
监视更改
可以使用 CODEOWNERS
功能来控制更改工作流文件的方式。 例如,如果所有的工作流文件都存储在 .github/workflows
中,可以将此目录添加到代码所有者列表,这样对这些文件的任何提议的更改都首先需要得到指定审阅者的批准。
有关详细信息,请参阅“关于代码所有者”。
管理组织中 GitHub Actions 设置的权限
可以通过管理自定义组织角色,使用 GitHub Actions 对组织的 CI/CD 管道实施最低权限原则。 自定义组织角色是为组织中的个人或团队授予控制特定设置子集的方法,而不用授予组织及其存储库完整管理员控制。
对于 GitHub Actions,可以为组织中的个人或团队启用以下任何权限。
- 管理组织操作策略: 访问以管理“操作常规”设置页上的所有设置,但自托管运行器设置除外。
- 管理组织运行器和运行器组: 访问并创建和管理 GitHub 托管的运行器、自托管运行器和运行器组,并控制可以创建自托管运行器的位置。
- 管理组织操作机密:访问并创建和管理操作组织机密。
- 管理组织操作变量:访问并创建和管理操作组织变量。
有关详细信息,请参阅“关于自定义组织角色”。
使用 OpenID Connect 访问云资源
如果 GitHub Actions 工作流需要访问支持 OpenID Connect (OIDC) 的云提供商提供的资源,则可以将工作流配置为直接向云提供商进行身份验证。 这样就可以停止将这些凭据存储为长期存在的机密,并提供其他安全优势。 有关详细信息,请参阅“OpenID Connect”。
注意
AWS 不支持 OIDC 的自定义声明。
使用 Dependabot version updates 使操作保持最新
可使用 Dependabot 来确保对存储库中使用的操作和可重用工作流的引用保持最新。 操作通常使用漏洞修复和新功能进行更新,以使自动化流程更快速、更安全、更可靠。 Dependabot 使你无需维护依赖项,因为其会自动执行此操作。 有关详细信息,请参阅 使用 Dependabot 保持操作的最新状态 和 关于 Dependabot 安全更新。
允许工作流访问内部和专用存储库
如果使专用存储库可供其他存储库中的 GitHub Actions 工作流访问,则其他存储库中的外部协作者可以间接访问专用存储库,即使他们没有直接访问这些存储库的权限。 当使用来自专用存储库的操作或工作流时,外部协作者可以查看工作流运行的日志。 有关详细信息,请参阅“与企业共享操作和工作流”。
为了允许运行器下载这些操作,GitHub 向运行器传递一个作用域内的安装令牌。 此令牌具有对存储库的读取访问权限,会在一小时后自动过期。
阻止 GitHub Actions 创建或批准拉取请求
可选择允许或阻止 GitHub Actions 工作流创建或审批拉取请求。 如果在没有适当监督的情况下合并拉取请求,允许工作流或任何其他自动化来创建或批准拉取请求都可能会带来安全风险。
有关如何配置此设置的更多信息,请参阅 “在企业中为 GitHub Actions 实施策略”、“为组织禁用或限制 GitHub Actions”和“管理存储库的 GitHub Actions 设置”。
使用 OpenSSF 记分卡保护工作流依赖项
记分卡是一种自动化安全工具,可标记有风险的供应链做法。 可以使用记分卡操作和工作流模板来遵循最佳安全做法。 配置完成后,记分卡操作将针对存储库更改自动运行,并使用内置的 code scanning 体验提醒开发人员有关有风险的供应链实践。 记分卡项目运行许多检查,包括脚本注入攻击、令牌权限和固定操作。
GitHub 托管的运行器强化
注意
GitHub Enterprise Server 目前不支持 GitHub 托管的运行器。 可以在 GitHub public roadmap 上查看有关未来支持计划的更多信息。
自托管运行器的强化
自托管运行器(适用于 GitHub)不能保证在临时干净的虚拟机中运行,并且可能会持续受到工作流中不受信任的代码的损害****。
要谨慎,因为任何可以创建存储库分支并打开拉取请求的人(通常是那些对存储库具有读取访问权限的人)都能够损害自托管运行器环境,包括获得对机密和 GITHUB_TOKEN
的访问权限,根据其设置,可以授予对存储库的写入权限。 尽管工作流程可以通过使用环境和必需的审查来控制对环境密钥的访问,但是这些工作流程不是在隔离的环境中运行,在自托管运行程器上运行时仍然容易遭受相同的风险。
企业所有者和组织所有者可以选择允许哪些存储库创建存储库级别的自托管运行器。 拥有“管理组织运行器和运行器组”权限的用户只能选择允许哪些存储库为组织中的存储库创建存储库级自托管运行器。
有关自定义组织角色的详细信息,请参阅“关于自定义组织角色”。
有关详细信息,请参阅 在企业中为 GitHub Actions 实施策略 和 禁用或限制组织的 GitHub Actions。
在组织或企业级别定义自托管运行器时,GitHub 可将多个仓库中的工作流安排到同一个运行器中。 因此,这些环境的安全危害可能会导致广泛的影响。 为了帮助缩小损害范围,可以通过将自托管运行器组织到单独的组中来创建边界。 你可以限制可访问运行器组的工作流、组织和存储库。 有关详细信息,请参阅“使用组管理对自托管运行程序的访问”。
您还应考虑自托管运行器机器的环境:
- 配置为自托管运行器的计算机上存储哪些敏感信息? 例如,私有 SSH 密钥、API 访问令牌等。
- 计算机是否可通过网络访问敏感服务? 例如,Azure 或 AWS 元数据服务。 此环境中的敏感信息量应保持在最低水平,您应该始终注意,任何能够调用工作流程的用户都有权访问此环境。
某些客户可能会尝试通过实施在每次作业执行后自动销毁自托管运行器的系统来部分降低这些风险。 但是,此方法可能不如预期有效,因为无法保证自托管运行器只运行一个作业。 有些任务将使用机密作为命令行参数,可以在同一运行器上的另一个任务中看到,例如 ps x -w
。 这可能导致机密泄露。
使用实时运行器
若要提高运行器注册安全性,可以使用 REST API 创建临时的实时 (JIT) 运行器。 这些自托管运行器在自动从存储库、组织或企业中删除之前,最多执行一项作业。 有关配置 JIT 运行器的详细信息,请参阅“自托管运行器的 REST API 终结点”。
注意
重新使用硬件托管 JIT 运行器可能会暴露来自环境的信息。 使用自动化来确保 JIT 运行器使用干净的环境。 有关详细信息,请参阅“自托管运行程序参考”。
从 REST API 响应获取配置文件后,可以在启动时将其传递给运行器。
./run.sh --jitconfig ${encoded_jit_config}
为自托管运行器规划管理策略
可以将自托管运行器添加到 GitHub 层次结构中的各个级别:企业、组织或存储库级别。 此布置决定了谁将能够管理运行器:
集中式管理:
- 如果您计划让一个集中的团队拥有自托管的运行器,则建议在最高的相互组织或企业级别添加您的运行器。 这为您的团队提供了一个位置来查看和管理您的运行器。
- 如果您只有一个组织,那么在组织级别添加运行器实际上是相同的方法,但如果将来添加另一个组织,则可能会遇到困难。
分散管理:
- 如果每个团队将管理自己的自托管运行器,则建议将运行器添加到团队所有权的最高级别。 例如,如果每个团队都拥有自己的组织,那么在组织级别添加运行器是最简单的。
- 您还可以在存储库级别添加运行器,但这会增加管理开销,并且还会增加所需的运行器数量,因为您无法在存储库之间共享运行器。
向云提供商进行身份验证
如果您使用 GitHub Actions 部署到云提供商,或者打算使用 HashiCorp Vault 进行机密管理,则建议您考虑使用 OpenID Connect 为工作流程运行创建短期、范围得当的访问令牌。 有关详细信息,请参阅“OpenID Connect”。
审核 GitHub Actions 事件
可以使用安全日志监视用户帐户活动,并使用审核日志监视组织或企业中的活动。 安全和审核日志记录操作类型、操作的运行时间以及执行操作的个人帐户。
例如,可使用审核日志跟踪 org.update_actions_secret
事件,这些事件跟踪组织机密的更改。
有关可在审核日志中找到的每种帐户类型的完整事件列表,请参阅以下文章:
了解工作流中的依赖项
可以使用依赖项关系图浏览存储库中工作流所使用的操作。 依赖项关系图是存储库中所存储之清单和锁文件的摘要。 它还将 ./github/workflows/
中文件识别为清单,这意味着使用语法 jobs[*].steps[*].uses
或 jobs.<job_id>.uses
引用的任何操作或工作流都将解析为依赖项。
依赖项关系图显示了有关工作流中使用的操作的以下信息:
- 拥有操作的帐户或组织。
- 引用操作的工作流文件。
- 操作固定到的版本或 SHA。
在依赖项关系图中,依赖项按漏洞严重程度自动排序。 如果使用的任意操作有安全公告,它将显示在列表顶部。 可以从依赖项关系图导航到公告,并访问用于解决漏洞的说明。
企业所有者可以为企业配置依赖项关系图和 Dependabot alerts。 有关详细信息,请参阅“为企业启用依赖项关系图”。
了解所使用的操作中的安全漏洞
有关市场上可用的操作,GitHub 会审查相关的安全公告,然后将这些公告添加到 GitHub Advisory Database 中。 可以搜索数据库来了解可用于查找现有漏洞信息的操作以及有关修复这些漏洞方式的说明。 若要简化搜索,请在 GitHub Advisory Database 中使用 GitHub Actions 筛选器。
可以设置存储库,从而:
- 在工作流中使用的操作收到漏洞报告时接收警报。 有关详细信息,请参阅“监视工作流中的操作”。
- 在工作流中添加或更新操作时,系统对现有公告发出警告。 有关详细信息,请参阅“筛选新工作流或更新工作流中的漏洞操作”。
监视工作流中的操作
可以使用 Dependabot 监视工作流中的操作,并启用 Dependabot alerts 以便在使用的操作有报告漏洞时通知你。 Dependabot 执行对启用存储库的默认分支扫描,以检测不安全的依赖项。 Dependabot 在向 GitHub Advisory Database 添加新公告或更新所使用的操作时,将生成 Dependabot alerts。
注意
Dependabot 仅针对使用语义化版本控制的有漏洞操作创建警报,且不会为固定到 SHA 值的操作创建警报。
需要先由企业所有者为你的企业设置 Dependabot,然后你才能管理你仓库的 Dependabot alerts。 有关详细信息,请参阅“为企业启用 Dependabot”。
你可以在存储库的 Dependabot alerts 选项卡中查看所有打开和关闭的 Dependabot alerts 以及对应的 Dependabot security updates。 有关详细信息,请参阅“查看和更新 Dependabot 警报”。
筛选新工作流或更新工作流中的漏洞操作
开立拉取请求来更新工作流时,最好使用依赖项评审来了解对所用操作所做更改的安全影响。 依赖项审查帮助您了解依赖项变化以及这些变化在每个拉取请求中的安全影响。 它提供了一个易于理解的依赖项变化可视化效果,多差异显示在拉取请求的“更改的文件”选项卡上。 依赖项审查告知您:
- 与发行日期一起添加、删除或更新的依赖项有哪些
- 有多少项目使用这些组件
- 这些依赖项的漏洞数据
如果对工作流所做的任意更改标记为了易受攻击,则可以避免将其添加到项目或将其更新为安全版本。
有关依赖项评审的详细信息,请参阅“关于依赖项评审”。
“依赖项审查操作”指的是可以在 GitHub Actions 上下文中报告拉取请求差异的具体操作。 请参阅 dependency-review-action
。 可使用存储库中的 依赖项审查操作 对拉取请求强制实施依赖项审查。 该操作会扫描拉取请求中包版本更改引入的易受攻击的依赖项版本,并警告你相关的安全漏洞。 这样可以更好地了解拉取请求中发生的变化,并帮助防止漏洞添加到存储库中。 有关详细信息,请参阅“关于依赖项评审”。
确保工作流中的操作安全且为最新版本
可使用 Dependabot 来确保对存储库中使用的操作和可重用工作流的引用保持最新。 操作通常使用漏洞修复和新功能进行更新,以使自动化流程更快速、更安全、更可靠。 Dependabot 使你无需维护依赖项,因为其会自动执行此操作。 有关详细信息,请参阅 使用 Dependabot 保持操作的最新状态 和 关于 Dependabot 安全更新。
以下功能可以自动更新工作流中的操作。
- Dependabot version updates 开立拉取请求,以在新版本发布时将操作更新到最新版本。
- Dependabot security updates 打开拉取请求,将带有所报告漏洞的动作更新为最低修补版本。
注意
- Dependabot 仅支持使用 GitHub 存储库语法(例如
actions/checkout@v4
)更新 GitHub Actions。 Dependabot 将忽略本地引用的操作或可重用工作流(例如,./.github/actions/foo.yml
)。 - 目前不支持 Docker Hub 和 GitHub Packages Container registry URL。 例如,不支持使用
docker://
语法引用 Docker 容器操作。 - Dependabot 支持 GitHub Actions 的公共存储库和专用存储库。 有关专用注册表配置选项,请参阅“Dependabot 选项参考”中的“
git
”。
有关如何配置 Dependabot version updates 的详细信息,请参阅“配置 Dependabot 版本更新”。
有关如何配置 Dependabot security updates 的详细信息,请参阅“配置 Dependabot 安全更新”。