密码学 04:HMAC 为什么需要秘密
服务端收到一个订单和一个 SHA-256 值,重新计算后两者一致。第 00、03 篇已经指出问题:攻击者能改订单,也能随手再算一个公开摘要。于是有人在订单与服务端之间增加了双方事先知道、攻击者不知道的密钥,再问:能否仅凭一串公开的认证值发现攻击者改过订单?可以,但要记住一个代价:接收者既然拥有同一把密钥,也有能力给任意新订单生成同样有效的值。 本篇把这种机制称为消息认证码(MAC),选择标准化的 HMAC-SHA-256 做最小实验。过去的文章有时把 HMAC 口语化地称为“对称密钥签名”;为了避免与私钥签、公钥验的数字签名混淆,这里固定叫 MAC。谁握有秘密、谁能验证与谁能伪造,是这一区别的核心。 换掉可公开重算的东西 设合成订单的字节已明确为 b"order=demo-001;quantity=1"。服务端和调用方事先通过可信渠道共享一把独立的密钥,消息通过可能受攻击者操控的链路送达。发送者计算 HMAC,把订单与 tag 一起交给接收者;接收者用同一把密钥重新计算并按安全接口比较。 123456789发送方(持有 K) ...
密码学 03:哈希能证明什么
下载一份账单,网页同时给出“账单的 SHA-256”。算完发现两串十六进制相等:能说这份文件是银行发的吗?如果下载页和摘要都被同一个攻击者替换,不能。同样地,把一笔交易的哈希值打印出来,既不证明是谁授权交易,也不证明它已经写入共同历史。摘要比较首先回答的是已知预期摘要时,当前输入是否与它对应;这里的“已知”必须说清来源。 在 00 里,攻击者同时换掉合成订单与公开预期摘要,相等检查仍返回真。02 又展示不同的字段组可以拼出完全相同的字节,不论使用哪种哈希函数都救不了未定义的字段边界。这篇再看哈希本身承诺哪类困难,以及三个经常被混称为“找到碰撞”的不同问题。 输入、输出和可信起点 对固定算法 SHA-256,输入是一串确切字节,输出是 32 字节。例如 b"abc" 的摘要为 ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad。输出的十六进制有 64 个字符,只是每个字节写成两个字符后的显示形式。该算法不使用秘密密钥,任何拿到消息的人都能重复计算。 123456作者文件字节 -- SH...
密码学 02:密码学处理哪些字节
订单页面上看见 quantity=1,不等于签名程序看到相同的消息。浏览器可以发送 JSON、表单字段或二进制协议;同一个 JSON 对象还可以有不同的空格和键顺序。如果一个端点对它收到的原始字节做摘要,另一个端点先解析再重新输出 JSON,即使两边都在谈“同一笔订单”,校验也可能失败。更糟的是,两个不同字段组合能拼出完全相同的字节:此时再强的哈希或签名算法也无法代替消息边界的约定。 第 00、01 篇讲“要保护什么”。这篇回答一个更靠前的问题:确切保护了哪些字节?输入没有说清,关于身份、用途和完整性的结论都容易越界。 从可读字符走到被验证的输入 假设订单文本里有中文“订单”。程序要先约定字符编码,例如 UTF-8,再确定字段顺序、数字表示、空白处理与字段长度,最后才可把结果交给摘要或签名操作。 123456789业务对象 {"sku":"book","quantity":1} | 指定字段、顺序、字符编码、长度和表示法 v确定的一串字节 --------> SHA-25...
密码学 01:窃听、篡改、冒充与重放怎样分开
浏览器向订单接口发 quantity=1。攻击者如果能看见消息,已经造成泄露;如果能把数量改成 9,是另一种失败;如果直接构造一条“来自用户 A”的新消息,前两个问题都不解释它;如果原封不动重发旧的有效订单,还会产生第四种失败。“消息安全”不是一个布尔值:必须先说明攻击者站在哪里、能做什么,接着逐项说出想阻止什么。 上一篇用摘要说明“当前消息和当前摘要相等”不能凭空证明来源。这篇继续使用同一合成订单,把四种攻击放在一个不受保护的教学通道上。它没有加密或签名:实验的作用是构造要阻止的失败,不是伪装成已经部署了安全协议。 给攻击者划出能力边界 在这个实验里,浏览器生成 JSON 字节;攻击者控制浏览器与教学接收者之间的一段明文通道。接收者相信 JSON 里的 sender 字段,但没有核对任何身份凭据。攻击者可读取字节、改动内容、插入新消息或记录后再发送。每种能力带来不同的问题。 1234567 可读、可改、可注入、可记录/转发浏览器 -- 原始订单字节 --> [ 网络中的攻击者 ] -- 任意字节 --> 接收者 ...
密码学 00:从 HTTPS 与交易两张图开始
把一笔订单从浏览器发往网站,再把一笔测试交易从钱包交给节点,这两件事都会出现“哈希、公钥、签名”。如果只记住算法名称,很容易做出一个危险的推论:网站已经有 HTTPS,订单自然是真的;交易验签成功,货物自然已经交付。两个推论都不成立。 本系列不从算法表开始,而从攻击者能做什么开始。本篇先看两条路径的责任边界,再拿固定订单字节做一个可以失败的摘要实验。图中的 TLS 与链节点是后续章节的研究对象:本篇没有运行 TLS 握手,也没有发送真实交易。 第一张图:订单发到 HTTPS 服务 设浏览器准备提交公开的合成订单 order_id=demo-001, sku=book, quantity=1。攻击者可能控制一段网络,能够观察、修改、转发旧数据,也可能提供一个伪装成服务端的地址。目标不是让订单“看起来复杂”,而是让客户端与服务端各自知道自己正在依赖什么。 12345678浏览器(预期服务名、可信 CA 配置) HTTPS 端点 | 连接 + 验证身份 / 协商临时密钥 | |<===== 双向 TLS ...
密码学 19:QUIC 如何复用 TLS 而自行保护数据包
浏览器走 HTTPS 提交 order=demo-001;quantity=1,如果服务改用 HTTP/3,连接依旧需要验证服务身份,也依旧需要协商密钥。常见的一句概括是“QUIC 跑在 UDP 上,内置 TLS 1.3”。这句话若被理解成“把 TLS 的一条记录装进一个 UDP 包”,就会把后续的包号、密钥更新和加密边界都画错。问题应具体到字节:TLS 交给 QUIC 的是什么?QUIC 自己对哪些字节做了什么? 从订单到数据包:谁执行哪一步 RFC 9001 将两件事分给不同组件。TLS 负责握手消息的语义:双方交换临时 key share,服务用证书及握手签名绑定它的身份,Finished 检查到目前为止的握手转录,随后得到不同方向、不同阶段的流量秘密。QUIC 承载握手字节,并用这些秘密派生自己的包保护密钥。TLS 的记录层不会为 QUIC 的订单创建 TLSCiphertext 记录。QUIC 用自己的帧、包号、头字段和 AEAD,把订单所在的流数据放进受保护的数据包中。 12345678浏览器 ...
密码学 21:VPN 的密钥怎样建立——IKEv2、IPsec 与安全关联
一台开发机经 VPN 访问测试网络中的 HTTPS RPC,使用钱包提交一笔无价值交易。出现三个“连接安全”的说法:VPN 已连接,HTTPS 握手成功,交易签名验过。它们保护的对象并不相同。VPN 先解决“这两个网络端点如何商定保护哪些 IP 包、用哪些方向的密钥”,不能因此替钱包签交易,也不保证远端 RPC 给出的区块状态可信。 先把通道建立者与数据保护者分开 IKEv2(RFC 7296)协商 IPsec 安全关联;ESP(RFC 4303)保护落到相应关联上的数据包。一个安全关联,简称 SA,不是一张写着“本 VPN 全部通信都安全”的通行证:它包含标识、算法、密钥、方向、序号等状态;可保护的流量还受流量选择器与路由影响。IKE 自己的 IKE SA 保护后续控制消息;给数据包用的是 Child SA。ESP 双向流量通常使用两条方向不同的 SA。 1234567开发机(发起方) 测试网关(响应方)IKE_SA_INIT: 提案、临时 DH 值、Ni ----> 选择提案、临时 DH 值、Nr ...
密码学 22:DNSSEC 与 DoH、DoT 各保护哪一段
浏览器打算向 orders.test 发一笔合成 HTTPS 订单。它先要把域名解析为地址。有人建议“启用加密 DNS”;有人建议“启用 DNSSEC”。如果客户端到解析服务的连接虽有 TLS,但解析服务给出了攻击者操纵的答案,该相信谁?反过来,如果能验证 DNSSEC 签名,路边的监听者就看不见域名查询了吗?两种建议回答的根本不是同一道题。 被保护的不是同一段 普通语言说,DoH/DoT 是对客户端与它选择的解析服务之间的传输建立加密、认证通道。DNSSEC 是让有能力验证的解析方核对某个域所有者签出的 DNS 数据来源和完整性。边界如图: 12345678客户端 stub -- DoH: HTTPS / DoT: TLS --> 选定的解析服务 ----> 权威 DNS │ 验证配置的 TLS 服务名 │ 解析器在 TLS 终点能看到问题 │ │ 可能自己做 DNSSEC 验证 └── 若自己验证 DNSSEC:从预置信任锚 ...
密码学 24:JWT、JWS 与 JWE——可读令牌、签名令牌和加密令牌
订单 API 收到一个形如 xxxxx.yyyyy.zzzzz 的访问令牌。开发者看到第三段是签名,就把第二段用户信息当成“被加密过的秘密”;又有人看到验签通过,就跳过了这个令牌到底签给哪个服务、什么时候过期的检查。攻击者完全可以复制一个仍在有效期内的 bearer token,带着它走一条自己的 TLS 连接。问题并不是 JWT 这种格式“安全还是不安全”,而是把不同操作解决的问题说成了一回事。 看一个令牌时先问:它做了哪种操作 JWT 是在各方之间传递声明(claims)的格式及处理约定。常见 iss 标明发行者,aud 标明预期使用者,exp 给出过期时刻。JWT 可以承载为 JWS(签名或 MAC 保护),也可以承载为 JWE(加密),还可能有嵌套组合;不是看到三个点分隔段就能断言所有 JWT 都加密了。 JWS compact 有三段:受保护头、payload、签名。前两段是 base64url 编码;编码不提供保密性,拿到令牌的一方即使没有公钥,也能读出 payload 的 JSON 声明。持有者可能仍因未通过签名验证而不能可信地采用这些声明;可读与可信是两件不同的事...
密码学 25:WebAuthn 与 passkey——网站验证什么,用户验证在哪里
一个站点希望用 passkey 登录后,再让用户提交合成 HTTPS 订单。屏幕上似乎只点了一次“用设备解锁”,服务器却要判断三件不同的事:这是先前注册的凭据吗?这个凭据是在本网站发起、且回应的是这次挑战吗?认证器是否按站点政策完成了用户验证?单说“用了非对称密码学”无法回答其中任何一道题。 凭据私钥在哪,服务器保存什么 WebAuthn 的注册阶段,认证器针对一个 relying party(RP,依赖方,例如 orders.test)创建凭据,私钥由认证器或平台的凭据管理机制持有;服务端保存凭据标识与公钥,并把它关联到允许登录的账户及 RP。注册的来源、证明政策、哪些公钥可以保存,本身需要单独审查。下面实验预置一条合成注册记录,因而只能讨论登录断言,不能顺带宣称已验证真实注册或 attestation。 登录时服务端先生成一次性、不可预测的 challenge,浏览器提供站点 origin 信息并请求认证器生成断言。认证器对自身的 authenticatorData 加上浏览器产生的 clientDataJSON 字节的 SHA-256 摘要签名: 12345678服务端:生...












