静态制品是一个目录,而不是单个可执行文件。只记录源码提交或首页校验和会留下缺口:缺失的图片、陈旧别名或变化的下载可能悄悄留下来。传输前,应冻结制品并枚举其中每个常规文件。

冻结的常规文件制品被传输到目标端,两端分别生成位于制品树外的有序清单。目标清单中多出示意的 old.js 记录;两份清单汇入完整记录比较。另一条分支对源端清单做哈希以生成发布标识。逐项校验不能发现额外文件,字节一致也不能证明作者身份。
测量前停止写入、拒绝符号链接和特殊文件,并将清单保存在制品树外。逐项校验发现缺失或变化的字节;重新生成并比较完整清单还可发现多余文件。对可信源端清单做哈希可得到紧凑标识,真实性与元数据仍需独立控制。

生成稳定的列表

从包含 public/ 的目录,在 Bash 中使用 GNU findutils 与 coreutils 运行以下命令。清单应保存在被测目录之外。发布只能包含目录和常规文件:先检查并拒绝符号链接或特殊文件,因为它们不在该清单的契约内。

set -euo pipefail
manifest="$PWD/release.sha256"
(
  cd public
  nonregular=$(find . ! -type d ! -type f -print -quit)
  test -z "$nonregular"
  find . -type f -print0 | LC_ALL=C sort -z | xargs -0 -r sha256sum --
) > "$manifest"
test -s "$manifest"

先排序文件名,再计算哈希。工具之间用 NUL 分隔路径,可保留空格、制表符与内嵌换行;GNU sha256sum 会在普通可校验输出中转义特殊文件名。不要用 awk '{print $2}' 解析该输出:文件名中可以合法包含空白。单独赋值会保留 find 失败时的退出状态;只检查其输出,可能把扫描失败误认成空结果。扫描或管道一旦失败,就应把清单视为不完整,不得发布。测量期间停止写入;这些命令不会创建文件系统快照。

传输后验证

在目标端的同一个可丢弃 Bash 会话中,按顺序运行后续片段,使路径变量与 set 选项保持生效。将清单与制品路径设为实际位置:

set -euo pipefail
manifest=/path/to/release.sha256
artifact=/srv/www/site.new
(
  cd "$artifact"
  sha256sum -c "$manifest"
)

检查列出的文件能发现缺失或改变的字节,却不能发现多余文件。因此按相同流程重新生成目标端清单,并比较完整记录:

observed=$(mktemp "${manifest}.observed.XXXXXX")
trap 'rm -f "$observed"' EXIT
(
  cd "$artifact"
  nonregular=$(find . ! -type d ! -type f -print -quit)
  test -z "$nonregular"
  find . -type f -print0 | LC_ALL=C sort -z | xargs -0 -r sha256sum --
) > "$observed"
diff -u "$manifest" "$observed"

没有差异且退出状态为零,表示常规文件的路径与哈希一致。多出一个陈旧文件也会使检查失败。临时输出位于制品之外,因此检查不会包含自身。空目录、权限、所有者与时间戳不在测量范围内;部署依赖这些属性时需另行验证。

为发布记录增加聚合摘要

对清单本身做哈希,得到紧凑的发布标识:

sha256sum "$manifest"

其含义依赖固定的规则:相对路径、排序区域、算法、工具格式,以及包含哪些元数据。将可信清单与源码提交、构建器版本一起保存。

不要把完整性等同于真实性

SHA-256 摘要一致,是字节一致的强实用证据,并不能证明作者身份。攻击者若能同时替换制品与清单,也能让检查通过。签名与可信分发是独立控制;只应校验通过预期可信渠道取得的清单。

来源