Posted in

GitHub上发布Go项目却无法通过OpenSSF Scorecard 4.0?逐项破解代码签名、branch protection、automated-security-tools等12项指标

第一章:GitHub上发布Go项目的OpenSSF Scorecard 4.0合规总览

OpenSSF Scorecard 4.0 是开源软件安全基金会推出的自动化安全健康度评估工具,专为 GitHub 仓库设计,全面检查项目在开发流程、依赖管理、代码审查、签名验证等维度的实践成熟度。对于 Go 语言项目,Scorecard 不仅识别 .go 文件和 go.mod 结构,还深度集成 Go 生态特有机制(如 gofumpt 格式化、govulncheck 漏洞扫描、cosign 签名验证)以提供精准评分。

安装与本地运行 Scorecard

确保已安装 scorecard CLI(v4.0+)及 Go 工具链:

# 使用 Go install 安装最新稳定版(需 Go 1.21+)
go install github.com/ossf/scorecard/v4/cmd/scorecard@latest

# 验证版本(输出应含 v4.x.x)
scorecard --version

执行扫描时需提供 GitHub 令牌以访问私有 API(如启用 Dependabot 或 Code Scanning):

# 扫描公开 Go 仓库(替换为你的仓库 URL)
scorecard --repo=https://github.com/your-org/your-go-project \
          --show-details \
          --format=json > scorecard-report.json

关键 Go 专属检查项

Scorecard 4.0 对 Go 项目启用以下高价值检查(默认激活):

  • Pinned Dependencies:验证 go.mod 中所有依赖是否使用精确语义化版本(非 latest 或通配符)
  • Vulnerability Detection:调用 govulncheck 扫描模块依赖中的已知 CVE(需 GOOS=linux GOARCH=amd64 govulncheck 可用)
  • Signed Releases:检查 GitHub Release 是否使用 cosign 签名,并在 release.yml 中配置 sign-release 步骤
  • Automated Security Testing:识别是否在 CI(如 GitHub Actions)中集成 gosec 或 staticcheck

合规提升建议

检查项 推荐实践
Branch Protection 启用 main 分支保护规则:要求 PR 审查、状态检查通过、线性提交历史
Code Review 在 .github/workflows/ci.yml 中添加 reviewdog 或 golangci-lint 检查
Dependency Updates 配置 Dependabot 自动更新 go.mod,并启用 security_updates: true

项目根目录应包含 SECURITY.md 和 CODE_OF_CONDUCT.md,且 go.mod 文件必须存在——Scorecard 将据此判定为有效 Go 项目并启用全部语言特异性检查。

第二章:代码签名(Signed Releases)的深度实践

2.1 Go模块签名机制与cosign原理剖析

Go 模块签名依托于 go.sum 文件的校验机制,但其本身不提供强身份认证。cosign 填补了这一空白,通过基于 Sigstore 的透明日志(Rekor)和密钥管理(Fulcio)实现可验证的签名。

签名流程核心组件

  • Fulcio:颁发短期证书(X.509),绑定 OIDC 身份(如 GitHub Actions)
  • Rekor:不可篡改的签名存证账本,支持二分查找验证
  • Cosign CLI:统一接口封装签名/验证/审计操作

cosign 签名示例

# 使用 OIDC 身份自动签发证书并签名模块包
cosign sign --oidc-issuer https://token.actions.githubusercontent.com \
            --fulcio-url https://fulcio.sigstore.dev \
            --rekor-url https://rekor.sigstore.dev \
            ghcr.io/myorg/mymodule@sha256:abc123

此命令触发三步链式操作:① 向 Fulcio 请求 OIDC 认证证书;② 用私钥签名模块 digest;③ 将签名+证书+公钥哈希写入 Rekor。--rekor-url 参数确保所有签名公开可查,实现供应链透明性。

组件 协议/标准 关键保障
Fulcio X.509 + OIDC 身份绑定、证书时效性
Rekor Merkle Tree 签名防篡改、存在证明
Cosign CLI OCI Artifact 兼容容器生态与模块包
graph TD
    A[开发者执行 cosign sign] --> B[Fulcio 颁发短期证书]
    B --> C[本地私钥签名模块摘要]
    C --> D[上传签名+证书到 Rekor]
    D --> E[生成可验证的 OCI artifact]

2.2 在GitHub Actions中自动化生成和验证SLSA3级签名

SLSA3 要求构建过程受控、可重现且具备完整 provenance(来源证明),GitHub Actions 是实现该等级的理想执行环境。

核心依赖组件

  • slsa-framework/slsa-github-generator: 官方认证的 Go 构建器(.github/workflows/build.yml)
  • sigstore/cosign: 签名与验证工具
  • actions/checkout@v4 + actions/setup-go@v5: 确保源码与构建环境一致性

构建与签名工作流节选

- name: Generate SLSA provenance and sign
  uses: slsa-framework/slsa-github-generator/.github/workflows/builder_go_v1.yml@v1.12.0
  with:
    binary: ./bin/app
    upload-assets: true

此动作自动触发 slsa-verifier 验证构建环境完整性,生成符合 SLSA Spec v1.0 的 slsa-provenance.json,并用 GitHub OIDC 签发短时效证书签名二进制与 provenance。binary 指定输出路径,upload-assets 启用 GitHub Release 关联发布。

验证流程(mermaid)

graph TD
  A[下载 release assets] --> B[cosign verify-blob --certificate-oidc-issuer https://token.actions.githubusercontent.com --certificate-identity-regexp '.*github\.com/.*/.*' bin/app]
  B --> C{验证通过?}
  C -->|是| D[确认 provenance 签名 & 构建链完整性]
  C -->|否| E[拒绝执行]
验证项 工具 输出示例
二进制签名 cosign verify-blob Verified OK
Provenance 结构 slsa-verifier verify-artifact PASSED: Verified SLSA level 3

2.3 使用fulcio+rekor构建可审计的签名基础设施

Fulcio 是一个 OIDC 驱动的证书颁发机构,专为软件供应链签名设计;Rekor 则是不可篡改的透明日志,用于存证签名事件。二者协同构成零信任签名基础设施的核心。

核心组件职责

  • Fulcio:签发短期 X.509 证书(默认10分钟),绑定 OIDC 身份(如 GitHub Actions OIDC token)
  • Rekor:接收并持久化签名条目(tlog entry),提供全局可验证、可查询的审计线索

签名流程示意

graph TD
    A[开发者触发 CI] --> B[OIDC Token 向 Fulcio 申请证书]
    B --> C[Fulcio 验证身份并签发证书]
    C --> D[cosign sign -cert -key -oidc-issuer ...]
    D --> E[签名 + 证书 + payload hash 写入 Rekor]
    E --> F[Rekor 返回唯一 UUID 和 tlog index]

示例签名命令

cosign sign \
  --fulcio-url https://fulcio.sigstore.dev \
  --rekor-url https://rekor.sigstore.dev \
  --oidc-issuer https://token.actions.githubusercontent.com \
  ghcr.io/myorg/mymodule:v1.0.0

--fulcio-url 指定证书颁发端点;--rekor-url 声明日志写入位置;--oidc-issuer 必须与 OIDC token 的 iss 声明严格匹配,否则 Fulcio 拒绝签发。

组件 审计能力 数据持久性
Fulcio 仅保留证书签发元数据(无签名体) 临时缓存
Rekor 完整存储签名、证书、哈希、时间戳 永久、Merkle 化

2.4 修复go.dev/pkg签名状态缺失的典型错误链

根因定位:签名元数据未随模块同步

go.dev/pkg 依赖 index.golang.org 提供的签名状态(sigstore/rekor 索引),但常见错误是 go list -m -json 输出中缺失 Signatures 字段。

关键修复步骤

  • 确保模块发布时启用 GOSUMDB=sum.golang.org(默认)且未被代理拦截
  • 检查 go.mod 中 go 1.21+ 版本声明(签名验证自 1.21 起强制增强)
  • 验证 GOPROXY 未跳过校验(如 GOPROXY=https://proxy.golang.org,direct 合法;GOPROXY=direct 则丢失签名元数据)

签名状态获取示例

# 查询模块签名状态(需 go 1.22+)
go list -m -json -versions rsc.io/quote@v1.5.2

此命令触发 index.golang.org/v1/lookup HTTP 请求,返回含 "Signatures": [...] 的 JSON。若字段为空,表明索引服务尚未同步该版本的 Sigstore 签名条目。

字段 含义 缺失影响
Signatures Sigstore 公钥签名哈希列表 go.dev/pkg 显示“Unsigned”红标
Origin 模块源仓库 URL 影响可信来源判定
Timestamp 签名时间戳 决定是否接受过期签名
graph TD
    A[开发者推送 v1.5.2] --> B{go.dev/pkg 轮询 index.golang.org}
    B --> C[发现新版本但无签名条目]
    C --> D[显示“Signature status: missing”]
    D --> E[手动触发 rekor CLI 提交签名]

2.5 签名策略与语义化版本标签的协同治理

签名策略需与语义化版本(SemVer)标签深度耦合,确保每次 vX.Y.Z 发布均携带不可篡改的可信凭证。

签名验证流程

# 使用 Git tag 签名 + SemVer 校验脚本
git verify-tag v1.2.0 && \
  semver --validate $(git describe --tags --exact-match)
  • git verify-tag 验证 GPG 签名有效性;
  • semver --validate 检查标签是否符合 MAJOR.MINOR.PATCH 格式及递增逻辑。

协同校验规则

触发事件 版本变更要求 签名强制级别
PATCH 更新 允许热修复,需签名 强制
MINOR 新增 向后兼容,需双签 强制+审计日志
MAJOR 不兼容 需三方会审+签名链 强制+CA背书

自动化流水线协同

graph TD
  A[Git push tag v2.1.0] --> B{SemVer 格式校验}
  B -->|通过| C[调用 GPG 签名服务]
  B -->|失败| D[拒绝推送]
  C --> E[写入签名元数据至 OCI registry]

第三章:分支保护(Branch Protection)的Go项目适配

3.1 Go monorepo场景下的main/main-v2/develop多分支保护策略

在大型 Go monorepo 中,main(稳定发布)、main-v2(v2 API 过渡)与 develop(集成预演)三分支并行演进,需差异化保护。

分支语义与准入规则

  • main:仅允许经 CI 验证的 tag 推送,禁止直接 push
  • main-v2:需 v2/ 目录变更 + v2 兼容性测试通过
  • develop:允许 PR 合并,但须通过全部单元与集成测试

GitHub Actions 分支保护配置示例

# .github/workflows/branch-protection.yml
name: Branch Protection
on:
  pull_request:
    branches: [main, main-v2, develop]
jobs:
  enforce:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Validate Go modules
        run: go list -m all 2>/dev/null || exit 1

此检查确保 PR 修改未破坏模块依赖图;go list -m all 验证 go.mod 可解析性,避免隐式版本漂移。

CI 策略对比表

分支 必需检查项 合并阻断条件
main e2e 测试、CVE 扫描、签名验证 任一失败即拒绝合并
main-v2 v2 接口契约测试、双栈路由兼容性 v2/ 外路径修改需额外审批
develop 单元测试覆盖率 ≥85%、gofmt 校验 覆盖率下降超 0.5% 报警
graph TD
  A[PR 提交] --> B{目标分支}
  B -->|main| C[触发 release-candidate pipeline]
  B -->|main-v2| D[注入 v2-contract-validator]
  B -->|develop| E[运行 quick-test suite]
  C --> F[签署并归档]
  D --> G[生成兼容性报告]
  E --> H[实时覆盖率反馈]

3.2 基于go mod graph的依赖变更自动触发保护规则升级

当模块依赖图发生变更时,需实时感知并触发对应安全策略升级。核心机制基于 go mod graph 的结构化输出解析:

# 生成当前模块的完整依赖有向图(含版本)
go mod graph | grep "github.com/org/pkg@v1\."

该命令输出形如 main@v0.0.0 github.com/org/pkg@v1.2.3 的边关系,可作为依赖变更的原子信号源。

依赖变更检测流程

  • 每次 CI 构建前执行 go mod graph 快照比对
  • 差异项经白名单过滤后,映射至预定义的规则集(如 CWE-798 对应 golang.org/x/crypto/bcrypt 升级)

规则升级决策表

依赖包 风险等级 触发动作 最低安全版本
golang.org/x/text HIGH 强制升级 + 扫描 v0.14.0
github.com/gorilla/mux MEDIUM 警告 + 人工复核 v1.8.0
graph TD
  A[go mod graph] --> B[解析边关系]
  B --> C{是否新增高危依赖?}
  C -->|是| D[加载对应保护规则模板]
  C -->|否| E[跳过]
  D --> F[注入版本约束与审计钩子]

3.3 GitHub Branch Protection API与go-github SDK集成实战

初始化客户端与认证

使用 github.NewTokenClient 构建带 OAuth Token 的 HTTP 客户端,确保具备 admin:org, repo 权限。

配置分支保护规则

opts := &github.ProtectionRequest{
    RequiredStatusChecks: &github.RequiredStatusChecks{
        Strict: true,
        Contexts: []string{"ci/test", "ci/build"},
    },
    RequiredPullRequestReviews: &github.PullRequestReviewsEnforcement{
        RequireCodeOwnerReviews: true,
        RequiredApprovingReviewCount: 2,
    },
    EnforceAdmins: &github.EnforceAdmins{Enabled: true},
}

该结构体映射 GitHub REST API /repos/{owner}/{repo}/branches/{branch}/protection 的完整 payload。Strict 控制合并前是否强制通过所有状态检查;Contexts 指定 CI 系统上报的检查名称;RequiredApprovingReviewCount 设定最小批准数。

触发保护策略更新

调用 client.Repositories.UpdateBranchProtection() 提交配置,失败时需捕获 *github.ErrorResponse 判断 404(仓库/分支不存在)或 403(权限不足)。

字段 类型 必填 说明
Strict bool 否 强制状态检查通过后才可合并
Contexts []string 否 CI 检查上下文名列表
RequireCodeOwnerReviews bool 否 是否要求代码所有者评审
graph TD
    A[Go程序] --> B[构造ProtectionRequest]
    B --> C[调用UpdateBranchProtection]
    C --> D{HTTP 200?}
    D -->|是| E[分支保护生效]
    D -->|否| F[解析ErrorResponse]

第四章:自动化安全工具(Automated Security Tools)集成方案

4.1 静态分析:gosec + staticcheck + govulncheck的CI流水线编排

在Go项目CI中,三工具协同可覆盖安全、规范与漏洞维度:

  • gosec:检测硬编码密钥、不安全函数调用(如http.ListenAndServe未启用TLS)
  • staticcheck:识别死代码、错误的并发模式、低效类型转换
  • govulncheck:基于Go中心漏洞数据库(GOVULNDB)实时扫描已知CVE

流水线执行顺序

# .github/workflows/static-analysis.yml
- name: Run static analysis
  run: |
    go install github.com/securego/gosec/v2/cmd/gosec@latest
    go install honnef.co/go/tools/cmd/staticcheck@latest
    go install golang.org/x/vuln/cmd/govulncheck@latest
    # 并行扫描,失败即中断
    gosec -fmt=json -out=gosec.json ./... && \
    staticcheck -checks=all -exclude=ST1005 ./... && \
    govulncheck -json ./...

-fmt=json统一输出便于后续解析;./...递归扫描全部包;&&确保短路执行。

工具能力对比

工具 检测重点 实时性 依赖更新
gosec 安全反模式 高 无需手动更新
staticcheck 代码质量与正确性 中 @latest自动拉取
govulncheck CVE级漏洞 依赖GOVULNDB同步 每日自动更新
graph TD
    A[源码提交] --> B[gosec扫描]
    A --> C[staticcheck扫描]
    A --> D[govulncheck扫描]
    B & C & D --> E[聚合报告 → GitHub Annotations]

4.2 依赖扫描:trivy-go + syft + GitHub Dependency Graph三重校验

现代软件供应链安全需多源交叉验证。单一工具易漏报(如仅静态解析忽略构建时注入)或误报(如未区分开发/生产依赖)。

三重校验逻辑

  • trivy-go:深度扫描 Go 模块的 go.sum 和 go.mod,识别已知 CVE;
  • syft:生成 SBOM(SPDX/ CycloneDX),覆盖二进制嵌入依赖;
  • GitHub Dependency Graph:实时同步仓库 go.mod 及 PR 变更,提供上下文感知依赖关系。

数据同步机制

# 同步 GitHub Dependency Graph 数据(需 GitHub App 权限)
curl -H "Authorization: Bearer $GITHUB_TOKEN" \
     -H "Accept: application/vnd.github.hawkgirl-preview+json" \
     https://api.github.com/repos/org/repo/dependency-graph/sbom

该 API 返回标准化 SPDX SBOM,用于比对 trivy/syft 输出差异项,自动标记“仅 GitHub 检出”或“仅 syft 检出”的依赖路径。

工具 覆盖维度 实时性 优势场景
trivy-go CVE + license 高 安全漏洞优先级排序
syft 构建产物 SBOM 中 容器镜像/离线环境审计
GitHub DG 仓库级依赖拓扑 秒级 PR 级细粒度影响分析
graph TD
    A[go.mod/go.sum] --> B(trivy-go)
    C[编译后二进制] --> D(syft)
    E[GitHub 仓库] --> F(GitHub Dependency Graph)
    B & D & F --> G[差异聚合引擎]
    G --> H[统一告警看板]

4.3 模糊测试:go-fuzz与afl++在Go核心包中的靶向注入实践

模糊测试是验证 Go 标准库鲁棒性的关键手段。以 net/http 包的 ParseHTTPVersion 函数为例,可构造精准语料进行靶向注入:

func FuzzParseHTTPVersion(f *testing.F) {
    f.Add("HTTP/1.1")
    f.Fuzz(func(t *testing.T, input string) {
        _, err := http.ParseHTTPVersion(input)
        if err != nil && !strings.Contains(err.Error(), "invalid") {
            t.Fatalf("unexpected error: %v", err)
        }
    })
}

该测试利用 Go 内置 fuzzing 框架,f.Add() 提供种子,f.Fuzz() 启动变异循环;参数 input 由 go-fuzz 自动变异生成,覆盖边界值(如 "HTTP/0.9"、"HTT P/1.1")。

工具 适用场景 Go 原生支持 靶向能力
go-fuzz 纯 Go 代码 ✅ 高(函数级)
afl++ (with goffi) 混合编译型目标 ❌(需插桩) 中(需符号化入口)
graph TD
    A[初始种子] --> B[go-fuzz 变异引擎]
    B --> C[编译为 instrumented binary]
    C --> D[覆盖率反馈驱动]
    D --> E[发现 panic / crash]

4.4 安全门禁:基于Scorecard结果动态阻断PR合并的Policy-as-Code实现

当 GitHub Scorecard 扫描完成,其 JSON 报告需实时驱动合并策略。核心是将合规性评估转化为可执行的准入策略。

数据同步机制

Scorecard 结果通过 Webhook 推送至 Policy Engine(如 Open Policy Agent),或由 CI 触发拉取:

# .github/workflows/policy-check.yaml
- name: Fetch Scorecard Result
  run: |
    curl -s "https://api.securityscorecards.dev/projects/github.com/$ORG/$REPO" \
      -H "Accept: application/json" > scorecard.json

此步骤获取权威、不可篡改的 Scorecard v4.3+ 原始数据;$ORG 和 $REPO 由上下文注入,确保租户隔离。

策略决策流

graph TD
  A[PR 提交] --> B{Scorecard API 调用}
  B --> C[解析 critical/low_score 指标]
  C --> D[OPA evaluate policy.rego]
  D -->|deny if score < 70| E[拒绝合并]
  D -->|allow if all checks pass| F[批准合并]

策略规则示例

检查项 阈值 阻断动作
Token-Permissions ≥ 80 否则禁止合并
Signed-Tags ≥ 90 否则标记高风险
# policy.rego
package github.pr

import data.scorecard

default allow := false
allow {
  scorecard.token_permissions >= 80
  scorecard.signed_tags >= 90
}

scorecard.token_permissions 来自 JSON 中 checks[i].score,单位为百分制整数;策略仅在所有关键检查达标时返回 true。

第五章:从Scorecard 4.0到SLSA Level 3的演进路径

Scorecard 4.0核心检查项与SLSA Level 3能力映射

Scorecard 4.0包含18项自动化检查(如Signed-Releases、Pinned-Dependencies、Branch-Protection),其中12项直接支撑SLSA Level 3的四个支柱:出处(Provenance)、构建过程(Build Process)、依赖完整性(Dependency Integrity)和防篡改(Tamper Resistance)。例如,Code-Review检查强制PR需双人批准,对应SLSA要求“所有变更经授权团队审查”;Automated-Tests覆盖率达85%以上,则为SLSA构建可重现性提供数据基线。

某云原生CI/CD平台实战升级路径

某金融科技公司使用GitHub Actions托管Kubernetes Operator项目。初始Scorecard得分为6.2/10,主要短板在Binary-Artifacts(未验证构建产物哈希)和Vulnerability-Alerts(依赖扫描未集成至PR流水线)。团队通过三阶段改造:第一阶段启用slsa-framework/slsa-github-generator/.github/workflows/builder_go_slsa3.yml模板生成SLSA3级Provenance;第二阶段将Trivy扫描嵌入on: pull_request触发器,并绑定GitHub Dependabot;第三阶段用Cosign对容器镜像签名,签名密钥由HashiCorp Vault动态分发。升级后Scorecard得分跃升至9.4,且通过SLSA Verifier v2.3.0验证全部17项Level 3断言。

关键差距分析表

Scorecard检查项 当前状态 SLSA Level 3要求 解决方案
Signed-Releases 仅Tag签名,无二进制签名 所有发布产物需独立签名 改用cosign sign-blob --key $KEY_PATH ./dist/app-linux-amd64
Fuzzing 未启用 构建流程需集成模糊测试 在build-and-test job中插入- name: Run go-fuzz<br> run: go install github.com/dvyukov/go-fuzz/go-fuzz@latest && go-fuzz -bin=./fuzz.zip -workdir=fuzz-corpus

构建环境加固实践

SLSA Level 3强制要求“隔离、不可变、最小化”的构建环境。该团队废弃自建Docker-in-Docker节点,转而采用GitHub-hosted runners + 自定义container image(基于cgr.dev/chainguard/wolfi-base:latest),该镜像仅含glibc、curl、git及Go 1.22,体积actions/checkout@v4配合persist-credentials: false禁用Git凭据继承,并在post:钩子中执行rm -rf $GITHUB_WORKSPACE/.git清除元数据。

flowchart LR
    A[Scorecard 4.0扫描] --> B{是否通过12项SLSA映射检查?}
    B -->|否| C[自动触发修复流水线:<br>- 注入SLSA Provenance生成步骤<br>- 启用Cosign密钥轮换策略]
    B -->|是| D[调用slsa-verifier verify-artifact<br> --provenance provenance.intoto.jsonl<br> --source github.com/org/repo]
    C --> E[重新提交PR并标记\"slsa-fix/\"

供应链透明度增强

团队将SLSA Provenance JSONL文件同步至专用Blob存储(Azure Blob Storage),设置SAS Token有效期为72小时,并在每次Release时向Slack频道推送结构化卡片,包含:构建时间戳、Git commit SHA、SLSA level、验证结果链接及cosign verify命令示例。运维团队通过curl -s https://blob.core.windows.net/provenance/2024.08.15.jsonl | jq '.predicate.buildDefinition'实时审计构建定义一致性。

审计日志留存机制

所有SLSA验证操作均写入OpenTelemetry Collector,采样率100%,字段包含slsa_level, verifier_version, provenance_hash, signature_status。日志经Fluent Bit过滤后持久化至Elasticsearch集群,保留周期365天,支持按repository_name和build_timestamp范围查询。审计人员可通过Kibana仪表盘查看过去30天SLSA验证失败TOP5原因分布,其中invalid-signature占比达63%,推动团队将Cosign密钥轮换周期从90天缩短至30天。

工具链版本锁定清单

  • GitHub Actions Runner: 2.315.0(固定patch版本,禁用自动升级)
  • Cosign: v2.2.1(SHA256: a1b2c3...f8e9d0)
  • SLSA Verifier: v2.3.0(通过go install github.com/slsa-framework/slsa-verifier/cmd/slsa-verifier@v2.3.0安装)
  • Trivy: v0.45.0(Docker镜像tag精确指定,非latest)

持续验证闭环设计

每日凌晨2点触发Cron Job,遍历所有已发布release tag,执行slsa-verifier verify-artifact并比对历史Provenance哈希。若发现哈希漂移,自动创建GitHub Issue并@security-team,Issue模板预填curl -s https://api.github.com/repos/{org}/{repo}/releases/tags/{tag} | jq '.assets[].browser_download_url'用于快速定位异常产物。

分享 Go 开发中的日常技巧与实用小工具。

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注