上方本地、跳板、目标路径保持目标认证从本地发起;下方远端套接字与本地代理交换请求和签名。

SSH 代理转发的权限边界

agent 转发并不等于 agent 本身。本地 agent 可以替客户端完成认证操作,而不把私钥交给服务器;转发则进一步在远端开放一个套接字,让那边的程序也能请求这项服务。因此,「私钥材料留在本地」和「身份认证能力只留在本地」是两个不同的承诺。 选择堡垒机访问方式时,这个区别尤其重要。如果只是要登录内网机器,通常用 ProxyJump 并关闭 agent 转发就够了。只有程序确实需要在远端发起认证,例如一条经过明确授权的后续 SSH 连接,才需要考虑把 agent 的使用能力交过去。不要把网络可达性和身份委托混成一个开关。 远端拿到的是能力 转发出来的套接字通常通过远端的 SSH_AUTH_SOCK 暴露。能访问它的进程,包括拥有足够权限的本机攻击者,都可以向原始 agent 请求操作。这不意味着服务器上的任何普通账户都有权限,更不意味着私钥文件已经被复制过去;风险来自可以调用身份,而不是必须先偷到密钥。 一把没有额外约束的已加载密钥,在转发通道仍然可用时,可能被拿去完成你没有打算批准的登录。密钥的存活期、逐次确认、目标限制,以及目标服务器是否接受这个身份,都会改变请求能否成功。把它说成「完整拥有私钥」不够准确,把它说成「只是一只无害套接字」同样会误导操作判断。 旧版本曾把一枚没有解释来源的八十八字节签名当作证据。单看长度,既不能证明实验实现了 OpenSSH agent 协议,也不能证明一次经过转发的认证成功。下面改用真正的 SSH 客户端,验证一个范围明确、读者可以复现的性质:最终生效的转发选项。这个检查不冒充真实网络集成测试。 转发由生效配置决定 命令里没有 -A,并不代表关闭了转发。匹配的 Host 段、被包含的配置文件,或者系统配置,都可能已经把它打开。反过来,即使文件写着 ForwardAgent no,命令行上的 -A 仍会显式启用转发。需要关闭时,应当使用 -a,或者没有相互矛盾选项的 -o ForwardAgent=no。 ProxyJump 提供到目标的 TCP 路径,并不会顺便重置目标连接的转发选项。目标 SSH 连接及其认证仍从本地发起,所以单纯借道跳板机不需要把 agent 暴露过去。目标和跳板机的配置也不一定一样,应分别检查相应别名,不要把某一条 ssh -G 的输出理解成整条路径的所有设置。 ...

二月 9, 2028 · 1 分钟 · XB Field Notes
颁发方签名响应先被缓存,再由客户端检查,并标出有限的新鲜度时间轴。

OCSP 装订按客户端策略交付状态证据

OCSP 装订让 TLS 服务端在握手过程中发送签名的证书状态响应。对于原本需要在线查询 OCSP 的客户端,它可能省去一次单独请求。收益取决于客户端的吊销策略:并非每个浏览器每次连接都查询 OCSP,也不是每个证书颁发机构仍然提供这种服务。装订只是传递状态证据的一种方式,不能代替完整证书验证,更不能把“握手收到了一段数据”当作信任已经成立。 传递证据并不转移信任责任 服务器从颁发机构或获得授权的应答方取回响应,并为适用握手缓存它。客户端仍须检查响应签名、签名者权限、证书标识、状态和可接受时间范围。服务器把响应附在握手里,不会让它自动可信。OCSP 的 good 状态也不能证明主机名匹配、证书仍在有效期内,或整条证书链已经连接到受信任根。这些验证职责必须分别保留。 线上表示还取决于 TLS 版本。TLS 1.2 使用状态请求扩展与 CertificateStatus 握手消息;TLS 1.3 则把响应放在对应 Certificate 条目的扩展中。把两者统称为 certificate_status 扩展,会混淆实际协议结构。普通网络攻击者不能随意剥离受认证握手消息,同时仍让 Finished 校验成功。讨论吊销检查被绕过时,应明确攻击能力和客户端策略,而不是虚构一种可以无痕删除握手数据的能力。 先核对支持范围,再决定启用 运维者首先需要确认颁发方提供可用响应,并检查服务端及客户端是否支持预期策略。Let’s Encrypt 已在二〇二五年八月六日结束 OCSP 服务,改用证书吊销列表。因此,对于没有 OCSP 支持的证书,缺少装订不自动意味着刷新任务损坏。应查当前颁发方文档,而不是把“所有站点都应该打开装订”当成不需要前提的清单步骤。 对于仍提供 OCSP 的颁发方,应在缓存过期之前留出刷新余量,并覆盖证书轮换。需要检查响应对应哪张证书、新鲜度及边缘实际交付内容,而不只是缓存是否存在。不同服务进程可能持有不同结果;控制面抓取成功也不证明全部已更新。缓存中的 good 响应仍可能在可接受窗口内,而较新的吊销信息已经出现,因此装订不能提供瞬时吊销知识。 坏掉的样子 缺失、过期、无效和已吊销是不同状态。客户端可能拒绝连接、查询其他机制,或应用明确的软失败策略。缺少响应不保证触发直连颁发机构,已经提供但无效的响应也不保证仅仅增加延迟。描述时应说明具体校验器的行为,不要承诺所有握手都会继续,更不能把“没有报错”直接解释为吊销状态已经得到确认。 ...

十二月 6, 2027 · 2 分钟 · XB Field Notes
受约束进程不能写只读根文件系统,但仍可使用指定的可写工作卷。

安全上下文需要运行时证据来验证

Kubernetes 的 securityContext 配置进程身份及部分操作系统约束,可以缩小被攻陷应用允许执行的操作,但本身不会建立完整安全边界。应先确定应用实际需要什么操作,再配置对应字段,并在最终容器中同时测试允许与拒绝的行为。只有一份看起来严格的清单,还不能说明运行中的进程已经按预期受到限制,也不能代替应用本身的权限设计。 原文 Kubernetes 1.34.11 历史样例,本次未重跑。结果反映组合设置;RuntimeDefault 取决于运行时。没有测试容器逃逸抵抗、全部 seccomp 规则、敌对多租户隔离或应用授权。执行仍为受阻。 把设置放在正确层级 Pod 级配置可以提供用户和组身份、共享卷的组权限行为,以及 seccomp 配置。容器级字段可以覆盖适用的默认值,并设置 capabilities、权限提升和只读根文件系统。需要核对字段真实 API 范围,把熟悉的名称写在错误位置,并不会获得等价约束。相同的 securityContext 名称之下,不代表所有字段都能互换放置。 ...

十一月 20, 2027 · 3 分钟 · XB Field Notes
非对称默克尔树在左侧合并 A 与 B,右侧单独为 C,上方为可信根。B 是目标,A 与 C 依次为这棵固定合成树的证明节点。

证书透明度让签发可被观察

证书透明度 CT 让公开 TLS 证书的签发变得可观察,但它不判断域名持有者是否授权了某次签发,不代替证书链验证,也不保证世界上所有证书都已经进入日志。一张错误签发的证书也可能带有有效的透明度证据。真正的价值是留下公共线索,供监控方和受影响的持有者调查。 承诺不等于包含证明 SCT 即签名证书时间戳,是日志对已提交证书或预证书作出的签名承诺:在最大合并延迟 MMD 以内把它纳入树中。SCT 不是另一张证书,也不是证明条目已经存在于树中的包含证明。它的时间戳记录日志作出声明的时刻,不一定就是证书实际签发的精确时刻。 默克尔包含证明把某个已经序列化的叶节点,连接到特定大小的树及其根哈希。验证者需要通过相应的已签名日志声明和可信日志公钥认证这个根。如果无条件接受不可信证明一起附带的根,攻击者完全可以自己构造另一棵树;计算结果相同,也没有建立任何有用的外部事实。 一致性证明回答的是另一个问题:后来的树是否在保持既有前缀不变的情况下扩展了早先的树。一次包含证明不能说明全部历史。签名检查点、一致性验证,以及独立观察者之间对不同视图的比较,有助于发现日志向不同人讲述不同历史。哈希让修改可以被这些检查发现,不是物理上禁止运营者改写数据。 Chrome 对公开受信任的 TLS 证书执行自己的 CT 策略,考虑 SCT 的交付方式、日志状态、数量与运营者多样性。这不是每次打开页面都到公共日志里搜索,也不是判断持有者是否申请过证书。RFC 9162 定义了第二版协议并废止 RFC 6962,但规范发布并不能直接证明某个已部署日志实际支持哪一版。 坏掉的样子 SCT 的签名即使有效,如果对应日志不被接受、数量不足或交付方式不符合目标客户端策略,仍然可能无法通过检查。反过来,符合 CT 也修复不了过期、不受信任或名称不匹配的证书。普通 TLS 验证、CT 合规和签发是否获准,是需要分别确认的三个决定。 域名监控也需要结合业务背景解释。新记录可能来自获准接入的 CDN、通配符证书或正常续签;升级告警之前应核对签发者、名称、指纹与内部签发清单。无法解释的签发应进入调查和纠正流程,不能假定“已经写进日志”就代表它被阻止或被确认合法。 公开日志可能暴露证书中的名称,因此不应仅为做实验就提交私有证书或敏感内部命名。监控覆盖不全、数据过旧,也不能证明某次签发不存在。运营上的监控要声明范围和更新时间,不能把一次空搜索结果当成所有日志、所有时间都没有相关证书的保证。 快速验证 保存为 ct_probe.go,使用 Go 1.23 或更新版本运行 go run ct_probe.go。程序离线使用 RFC 6962 的 SHA-256 默克尔构造,输入是三个合成字符串,不是真实证书,也不是已经按 CT 协议序列化的叶节点。它只验证这个固定三叶树中下标为一的中间叶节点,不是通用验证器。 ...

十一月 3, 2027 · 2 分钟 · XB Field Notes
通过校验的 SYN Cookie 仍需获得准入,才能进入独立的 accept 队列;应用仍须调用 accept()。

SYN Cookie 在队列溢出时作答

Linux TCP 监听端区分尚未完成的握手请求,以及已经建立但等待应用 accept() 的连接,通常称为 SYN 队列和 accept 队列。两者容量限制与溢出路径不同,排查连接故障时应先确定压力发生在哪个阶段,不能把所有等待都归到同一个 backlog 数字。 SYN Cookie 减少握手未完成期间需要保留的逐请求状态。服务器把可恢复信息编码到 SYN-ACK,收到有效回复后重建连接状态。节省的是待完成请求的内存,不代表处理没有开销,也不取消已建立连接的状态,更不保证一定准入或保持正常应用性能。 SYN Cookie 作用于握手。校验不会绕过 accept 队列,也不会增加应用接受能力。 两个队列,两种症状 普通有状态路径会保留请求,等待最后一个 ACK。Linux 文档将 tcp_max_syn_backlog 描述为每个监听端记住的请求上限;随内存变化的默认值与请求大小估算,不是所有机器的分配实测。等待应用接受的已建立连接,则由 listen() backlog 及 somaxconn 上限约束。Linux 从 5.4 起将 somaxconn 默认设为 4096,但部署值可以不同。 ...

十月 30, 2027 · 1 分钟 · XB Field Notes
时间轴区分过去经过正确认证的新鲜 ECDHE 会话与日后长期签名私钥失窃。在临时机密没有一起被获取的前提下,仅靠该长期私钥不能恢复先前会话机密。

TLS ECDHE 的前向保密边界

前向保密限制的是长期认证密钥日后泄露造成的影响。对于经过正确认证的临时 Diffie-Hellman 交换,攻击者仅取得长期认证私钥,不能据此恢复先前的会话机密。前提是临时机密没有一起被获取;它不会让已经被攻破的端点、导出的密钥日志或保存下来的明文突然变得安全。 新鲜协商、身份认证和密钥派生各司其职。前向保密只限制特定的长期签名私钥日后泄露场景,并不保护已经暴露的会话机密或被攻破的端点。 两类密钥承担不同职责 ECDHE 中,双方分别生成临时私钥并交换公开份额。每一方把本地私钥与对方公开份额组合,得到相同的共享机密。旁观者虽然能看到公开份额,但在算法的安全假设成立时,缺少重现计算所需的私有值。新鲜的交换材料并不是双方在网络上公开商量出的一段秘密口令。 TLS 也不会把原始 ECDH 输出直接当成应用加密密钥。密钥调度还会派生不同的握手与应用流量机密,绑定握手上下文,并区分方向和用途。临时交换只提供其中一项关键材料,不能用“双方算出相同字节”替代整个传输协议,也不能略过后续派生和认证步骤。 证书按照信任规则把身份绑定到公钥,实际签署握手材料的是对应的私钥,不是证书这个数据对象自己在签名。TLS 1.3 的证书认证路径通过 CertificateVerify 认证握手记录。如果没有身份认证,主动中间人可以替换公开份额,分别和两端建立自己的共享机密;仅仅看到加密正常,并不能排除这种攻击。 RSA 或 Ed25519 认证密钥可以与临时交换并存,签名算法和密钥交换组是不同选择。TLS 1.3 还定义了仅使用 PSK 的恢复模式,后者不具备 DHE 带来的这项前向保密;PSK 配合 DHE 则会加入新的交换材料。可选 early data 的保证更弱,因此不能单凭 TLS 版本或证书算法标签,就推断所有阶段的流量都有相同保护。 ...

十月 27, 2027 · 2 分钟 · XB Field Notes
按版本限定的输入混合与初始化计入连接生成器,非阻塞请求按就绪状态分支。

内核把设备噪声转成密钥

Linux 应用通常从内核的密码学安全生成器取得随机字节,不是每输出一位,就必须重新采集一位物理噪声。不过,初始化和状态复用仍然重要。机器繁忙不自动意味着安全,机器安静也不自动意味着故障。判断之前,应确认所用接口、内核版本,以及启动或克隆流程,不能只凭一个计数器就宣布密钥已经不安全。 区分采集与输出 Linux 6.12 的实现把输入混入基于 BLAKE2s 的池,并用 ChaCha20 生成输出。这是该版本的实现细节,不是所有未来版本永远不变的接口契约。硬件来源、中断时序和引导程序输入,按具体平台的信任规则参与。加入字节与把这些字节计入初始化熵估算,是两件不同的事;内核不能假设用户任意提供的一串字节都包含新鲜、不可预测的信息。 在这个实现中,poolsize 与初始化完成后的熵计数都达到二百五十六位,读取不会像消耗燃料一样扣减计数。因此,旧手册里四千零九十六位的描述,不应直接变成所有机器的告警阈值。应记录运行内核并检查对应行为,不能声称所有 Linux 版本的计数都一样,也不能仅因数字较小就判定随机输出薄弱。 选择会等待初始化的接口 默认 getrandom 会等到初始化完成。带 GRND_NONBLOCK 时,尚未初始化会报告 EAGAIN,使应用能够说明状态而不无限等待。初始化以后,默认源的一次请求若不超过二百五十六字节,具有文档规定的完整读取与信号保证;调用方仍应检查错误和返回长度。更大请求要正常处理部分返回和中断,不能把小请求的保证扩大到所有缓冲区。 random 设备从 Linux 5.6 起改变了行为:初始化后不再因为所谓熵耗尽而阻塞。在核对的 Linux 6.12 源码中,GRND_RANDOM 也不选择一个独立、会被耗尽的输出池。有些手册仍保留旧行为描述,不应把历史限制与当前内核结论混合。直接读取 urandom 可能发生在初始化之前;早期启动生成密钥,应使用支持初始化等待的接口。 坏掉的样子 早期启动仍可能缺少足够的可信计入输入。容器中缺少设备节点则是另一类接口问题:getrandom 不需要设备路径,普通容器共享宿主内核。操作等待时,应有明确的启动失败或等待策略,而不是改用时间戳、可预测种子或不安全的备用输出。让服务尽快启动,不是降低密钥生成要求的理由。 保存的种子可以帮助下一次启动,但把文件写回 urandom 并不会自动增加熵计数。一个三十二字节文件,或者某种硬件设备的存在,都不能单独保证立即就绪。应使用发行版提供的种子管理机制,了解它何时、依据什么信任条件计入熵。保护种子文件,避免在克隆镜像里分发相同的保存种子,并让引导程序专用的种子处理与操作系统流程一致。 复制磁盘镜像和恢复运行中的虚拟机,是两种不同风险。前者可能复制种子文件或已经生成的主机密钥,后者可能复制正在使用的生成器内存状态。新输入及虚拟机生成标识处理,可以让后续状态分离。未来输出完全相同是需要调查的风险,不是每次克隆必然出现的结果;两台机器使用同一份种子,也不足以证明它们全部生成器状态完全一致。 输入计入、初始化与输出各有边界。仅标明的 Linux 6.18.44 结果属于保留历史观察;本轮没有请求随机字节。 快速验证 在 Linux 上运行下面的 Python 3 脚本。它读取计数器,并请求一次非阻塞的三十二字节输出;不会打印随机内容,不读取私钥,不更改种子文件,也不需要管理员权限。这样可以观察接口是否就绪,而不把诊断过程变成密钥材料收集或系统配置变更。 ...

十月 22, 2027 · 1 分钟 · XB Field Notes
摘要与签名者绑定,构建来源则保留为独立证据。

镜像签名把摘要绑定到受信任签名者

容器镜像标签是可变指针,摘要则标识内容。有效签名把一份关于该摘要的声明绑定到签名密钥;这把密钥是否代表获授权的人或工作负载,要由信任策略判断。单靠签名,不能证明谁构建了镜像、构建是否遵循所宣称的流程,也不能证明软件内容安全。这些问题需要不同证据,不能因为校验成功就合并成一个结论。 真正有用的边界,是部署之前的验证决定。校验方需要检查内容绑定和预期信任根,再执行身份与来源策略。registry 地址、熟悉的标签,或者存在某份签名,都不足以独立构成放行依据。必须知道自己信任谁、允许其声明什么,以及这份声明究竟覆盖哪一个产物。 摘要是身份,标签是指针 OCI 清单摘要对精确清单字节进行哈希,清单中的描述符再通过各自摘要标识配置与层,客户端也须核对这些绑定。在哈希抗碰撞假设下,清单改变会得到不同摘要。如果只修改存储层中的字节却不更新描述符,应触发内容完整性失败,而不是悄悄把原镜像重新定义成另一份内容。内容寻址依赖实际校验,不只是路径看起来像摘要。 Cosign 的普通镜像签名载荷包含镜像摘要,验证时会把它与请求的镜像比较。不同标签可以指向同一个已签名摘要;标签一旦移动,先前的校验不能覆盖新对象。应解析并验证目标摘要,再部署同一个摘要。如果校验之后重新按标签拉取,就留下检查与使用之间的竞争窗口;重复查询标签也不能彻底关闭这个窗口。 标签字符串不能绑定镜像内容 对字面字符串 app:v2 签名,认证的是这个声明,并不是名字后面不断变化的对象。签名也可以包含其他上下文,所以需要检查真实载荷格式与校验器行为,而不是把所有方案都说成只签一个裸哈希。不要为了让不匹配的载荷通过,就关闭摘要声明校验,否则签名有效与目标镜像之间的关键联系会丢失。 基于密钥的校验,需要由策略说明为什么信任所给公钥。无密钥校验还要检查预期证书身份与 OIDC 颁发者,以及证书链和所要求的透明日志证据。Fulcio 证书把身份关联到密钥,不会检查构建过程或认证软件质量。要判断构建声明,还需要适当的来源证明、受信任的构建者,以及证明中主体摘要与待运行产物一致的检查。 校验才是控制,签名不是 强制检查可以放在准入或部署前,但效果取决于实际覆盖范围。需要确认哪些工作负载与 registry 被纳入策略,以及校验失败时如何处理。只记录摘要不能证明发布获授权;即使签名正确、身份也获授权,仍不能据此排除所有漏洞或恶意行为。安全测试与构建来源验证应各自提供证据,不能相互替代。 密码学签名有效与当前策略接受,是两回事。退役密钥需要更新所有相关验证方的信任策略,仅生成一把新密钥不会自动完成撤销。短期证书配合可信签名时间证据,可以支持证书到期之后验证历史签名。透明日志让活动可检查,却不会自动撤销泄露身份或阻挡恶意发布。事故响应仍须决定保留哪些身份、时间窗口、摘要和证明,不能把日志存在等同于风险已解决。 坏掉的样子 部署步骤先验证一个摘要,随后又按可变标签拉取;另一次推送就可能让最终运行的字节变掉。需要把经过验证的摘要一直传到部署交接处,并明确它指向单平台清单还是多平台索引,确认运行时怎样选择具体平台镜像。这样才能说明实际运行对象与被检查对象之间的关系。 另一种失败是接受任何能通过密码学检查的签名者。应把信任限制到获授权密钥,或明确的身份与颁发者组合,并测试错误签名者和未批准摘要会被拒绝。发现泄露后需要调查并改变策略;这并不自动证明该密钥以前签过的所有产物都恶意,也不能承诺继续接受它们一定安全。 镜像签名信任:机制与证据边界。 快速验证 下面是命令模板,不是可直接运行的夹具:registry 是示例,摘要也被省略。进行获授权的 registry 检查之前,需提供已有测试镜像的完整摘要、可信公钥,并确认所安装的 Cosign 与 Skopeo 版本。本次源码审阅没有签名、操作 registry 或执行部署,因此没有新的集成运行结果。 cosign verify --key cosign.pub registry.example.com/app@sha256:9ab5a134... cosign tree registry.example.com/app@sha256:9ab5a134... skopeo inspect --format '{{.Digest}}' docker://registry.example.com/app:latest 预期结果是指定摘要在选定密钥下通过校验,不一定打印可读的人类身份。cosign tree 发现关联对象,列出对象不等于验证其中声明。Skopeo 可以查看标签当下指向;如果不同,只说明它与刚才检查的摘要不同,另一个摘要仍可能有自己的签名。即使两者相同,也只是观察时刻的快照,无法阻止标签随后移动。 ...

十月 21, 2027 · 1 分钟 · XB Field Notes