深入解析sigstore-python:Python生态的软件签名革命

本文详细介绍了sigstore-python的首个稳定版本,这是一个为Python生态系统设计的Sigstore客户端实现,支持代码签名和验证,提升软件供应链安全,包含CLI工具和Python API的完整使用指南。

宣布sigstore-python稳定版发布 - The Trail of Bits博客

Trail of Bits很高兴地宣布sigstore-python的首个稳定版本发布,这是我们近一年来开发的Sigstore客户端实现!这项工作由谷歌开源安全团队(GOSST)慷慨资助,我们还与他们合作开发了pip-audit及其相关的GitHub Actions工作流。

如果您还不熟悉Sigstore,我们写了一篇解释文章,包括Sigstore是什么、如何在您自己的项目中使用它,以及像sigstore-python这样的工具如何融入整体代码签名生态系统。

如果您想开始使用,只需一个pip安装命令:

1
2
3
4
5
$ echo 'hello sigstore' > hello.txt
$ python -m pip install sigstore
$ sigstore sign hello.txt
$ sigstore verify identity hello.txt \
    --cert-identity 'foo@example.com' \

一个可用、参考质量的Sigstore客户端实现

我们对sigstore-python的目标有两个:

  • 可用性:sigstore-python应提供极其直观的CLI和API,具有100%的文档覆盖率和实际示例。
  • 参考质量:sigstore-python只是正在开发的许多Sigstore客户端之一,包括针对Go、Ruby、Java、Rust和JavaScript等生态系统的客户端。我们不是最老的实现,但我们的目标是在简洁和正确实现Sigstore安全模型的复杂性方面成为最权威的实现之一。

我们相信在这个版本中我们已经实现了这两个目标。本文的其余部分将展示我们是如何做到的!

可用性:sigstore-python适合所有人

sigstore CLI

Sigstore项目的座右铭之一是“为所有人提供软件签名”,我们希望sigstore-python忠实于这一点。为此,我们设计了一个公共Python API和sigstore CLI,将更模糊的加密部分抽象出来,留下几乎所有开发人员都已经熟悉的两个原语:签名和验证。

要开始,我们可以从PyPI安装sigstore-python,它作为sigstore提供:

1
2
3
$ python -m pip install sigstore
$ sigstore --version
sigstore 1.0.0

从那里,我们可以创建一个输入进行签名,并使用sigstore sign执行实际的签名操作:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ echo "hello, i'm signing this!" > hello.txt
$ sigstore sign hello.txt

等待浏览器交互...
使用临时证书:
-----开始证书-----
MIICwDCCAkagAwIBAgIUOZ3vPindiCHATxvCRQk/TC5WAd0wCgYIKoZIzj0EAwMw
NzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRl
cm1lZGlhdGUwHhcNMjMwMTEwMTkzNDI5WhcNMjMwMTEwMTk0NDI5WjAAMHYwEAYH
KoZIzj0CAQYFK4EEACIDYgAETb8dcUgXs31y6tjgsVy8KwfMEzVvhUVs7jlzcwkN
MLICjVvblYtWfFReYMEN8rM8mfglyAwcW+qY/I3klMnMcf/bna/yazzP7Mnnh1g1
dzlOXh14C9iZMDPIV0KHH5u2o4IBSDCCAUQwDgYDVR0PAQH/BAQDAgeAMBMGA1Ud
JQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBQdX9zi1TPEHw2uAkqaCE2ecWMLTDAf
BgNVHSMEGDAWgBTf0+nPViQRlvmo2OkoVaLGLhhkPzAjBgNVHREBAf8EGTAXgRV3
aWxsaWFtQHlvc3Nhcmlhbi5uZXQwLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRo
dWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbH
ETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGFnS1KGwAABAMARjBEAiAns85i
YPmlq9RWfJOUwCRN4y5Lwvk3/Y1cWB9wNW4XMwIgBRfib3YbotTgGpB16F/5uf7r
mO2Jc7e0yElimghFFmkwCgYIKoZIzj0EAwMDaAAwZQIxAOh0Ob8Mi2lENgRNjMRe
L8r8rBoVRSi8BzJHcKAe+eTwLsjvsdryJ0yKg5HVHc2erQIwNpdUXD71OPqs3QQ4
Ka+Q2Pjcs+GV5TvaecGzJuQGbm2J5ZW5raPJrXngEGUldt0U
-----结束证书-----

透明度日志条目创建于索引:10892071
签名写入hello.txt.sig
证书写入hello.txt.crt
Rekor包写入hello.txt.rekor

在您的桌面上,这将产生一个OAuth2流程,提示您进行身份验证,而在支持的CI提供商上,它将智能选择环境OpenID Connect身份!

这将产生三个输出:

  • hello.txt.sig:hello.txt本身的签名
  • hello.txt.crt:签名的证书,包含验证签名所需的公钥
  • hello.txt.rekor:一个可选的“离线Rekor包”,可在验证期间使用,而不是访问在线透明度日志

验证看起来几乎与签名相同,因为sigstore CLI根据输入的文件名智能定位签名、证书和可选的Rekor包。要实际执行验证,我们使用sigstore verify identity子命令:

1
2
3
4
5
$ # 找到hello.txt.sig, hello.txt.crt, hello.txt.rekor
$ sigstore verify identity hello.txt \
    --cert-identity foo@example.com \

OK: hello.txt

(这些额外标志是什么?没有它们,我们只会验证签名和证书,任何人都可以在Sigstore中获得任何公共输入的有效签名。为了确保我们实际上在验证有意义的东西,sigstore CLI强制您断言签名预期绑定到哪个身份,然后在证书验证期间进行检查!)

然而,这还不是全部!Sigstore不仅适用于电子邮件身份;它还支持URI身份,可以对应于特定的GitHub Actions工作流运行或其他机器身份。我们可以使用sigstore verify github子命令对这些签名进行更深入的验证,这允许我们检查GitHub Actions运行器环境所做的特定声明:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
$ # 更改为任何版本!
$ v=0.10.0
$ repo=https://github.com/sigstore/sigstore-python
$ release="${repo}/release/download"
$ sha=66581529803929c3ccc45334632ccd90f06e0de4

$ # 下载分发 + 证书和签名
$ wget ${release}/v${v}/sigstore-${v}.tar.gz{,.crt,.sig}

$ # 验证扩展声明
$ sigstore verify github sigstore-${v}.tar.gz \
    --cert-identity \
      "${repo}/.github/workflows/release.yml@refs/tags/v${v}" \
    --sha ${sha} \
    --trigger release

这远远超出了我们仅用裸sigstore verify identity命令可以证明的范围:我们现在断言签名是由针对提交66581529803929c3ccc45334632ccd90f06e0de4的发布触发的工作流运行创建的,这意味着即使攻击者以某种方式破坏了我们的存储库操作并为新输入签名,他们仍然无法欺骗我们接受此发布的错误签名!

全新的sigstore Python API

除了上面的CLI,我们还稳定了一个公共Python API!您可以使用此API执行sigstore CLI能够做的所有事情,以及更高级的验证技术(如“策略”的复杂逻辑链)。

使用与上面相同的签名示例,但使用Python API:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
import io

from sigstore.sign import Signer


contents = io.BytesIO(b"hello, i'm signing this!")

# 注意:identity_token()执行交互式OAuth2流程;
# 查看`sigstore.oidc`的其他成员以获取其他凭据机制。



signer = Signer.production()
result = signer.sign(input_=contents, identity_token=token)
print(result)

以及相同的基于身份的验证:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import base64
from pathlib import Path

from sigstore.verify import Verifier, VerificationMaterials
from sigstore.verify.policy import Identity

artifact = Path("hello.txt").open()
cert = Path("hello.txt.crt").read()
signature = Path("hello.txt.sig").read_bytes()
materials = VerificationMaterials(
    input_=artifact,
    cert_pem=cert,
    signature=base64.b64decode(signature),
    offline_rekor_entry=None,
)

verifier = Verifier.production()

result = verifier.verify(
    materials,
    Identity(
        identity="foo@example.com",

    ),
)
print(result)

Identity策略对应于sigstore verify identity子命令,并暗示了Python API表达声明之间更复杂关系的能力。例如,这里是我们如何编写上面的sigstore verify github验证:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from sigstore.verify import Verifier
from sigstore.verify.policy import (
    AllOf,
    GitHubWorkflowSHA,
    GitHubWorkflowTrigger,
    Identity
)

materials = ...

verifier = Verifier.production()

result = verifier.verify(
    materials,
    AllOf(
        [

            GitHubWorkflowSHA(
                "66581529803929c3ccc45334632ccd90f06e0de4"
            ),
            GitHubWorkflowTrigger("release"),
        ]
    )
)

…表示所有子策略之间的逻辑AND。

下一步是什么?

我们承诺对sigstore-python的API和CLI进行语义版本控制:如果您在Python项目中依赖sigstore~=1.0,您可以安全地假设我们不会在没有主要版本 bump的情况下进行破坏性更改。

考虑到这一点,稳定的API使我们能够在Python打包生态系统中实现许多近期的Sigstore目标:进一步集成到PyPI和客户端打包工具链中,以及稳定我们相关的GitHub Action。

与我们合作!

Trail of Bits致力于Sigstore生态系统的长期稳定和扩展。如果您想参与Sigstore或正在与您的公司合作将其集成到您自己的系统中,请联系我们!

comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计