密码学 08:密码为什么不能直接做 SHA-256
数据库只存 SHA-256(密码),不存密码原文。泄漏后,攻击者是不是就无法还原用户密码了?如果密码是 book123 这样的小字典成员,攻击者拿到摘要,离线把常见猜测逐个 SHA-256 后比较即可;不需要攻击 SHA-256 的原像困难假设。输入空间太小,找目标输入并不贵。换一把更长的密码当然有帮助,但服务端存储方案仍必须考虑泄漏后的离线猜测成本、多用户复用猜测和参数演进。 前面 03 篇用 SHA-256 处理公开订单字节,这是合适的教学摘要任务。密码不是随机的高熵消息,不能把安全哈希函数对一般原像的困难性直接套在用户选择的短密码上。本篇用真实 Argon2id 库对照 SHA-256 的有限字典:同一个合成弱密码,两种方案最终都能猜到;区别在于 Argon2id 允许服务端设置验证所需的内存、遍数等成本,而不是承诺“弱密码永远猜不出来”。 攻击者读到什么,谁持有秘密 注册时服务端得到密码,生成一份各条记录独立的 salt,记录盐、算法版本、成本参数与密码哈希输出。登录时用这条记录的参数再算一次,按安全接口比较。数据库一旦泄漏,攻击者可离线读取盐、算法及参数,还能对候选密码...
密码学 07:AEAD 怎样保护内容与上下文
订单内容经加密后谁也看不懂,攻击者却把原本属于 POST /orders 的密文交给另一个接口,或者把标记身份的请求上下文换成了另一个用户。如果接收端只解密、不核对这些可见的上下文与被保护的消息是否同属一笔操作,就可能处理错误对象。第 06 篇展示 ECB 会泄漏重复结构,教学 XOR 也说明不带校验的密文可能被改。这篇直接用成熟库试一个提供保密性与完整性检查的接口:AEAD。 本章不实现 TLS。它执行真实 AES-GCM 原语的固定向量以及正确解密、改密文、改标签、改上下文和故意重复 nonce 的失败对照。它证明的是所选库、所选输入下的行为,不能凭一个原语实验宣布整个 HTTPS 订单服务安全。 五类输入,三类输出与一条失败路径 设服务和客户端已安全约定一个对称密钥 K,并在这一把密钥的生命周期里不重复使用加密 nonce。调用者将确切的订单字节作为明文,把服务方法、路径和已认定用户等不需加密、却不能被错配的字节作为 AAD(附加认证数据)。这里使用 AES-GCM;得到密文和认证标签。接收者传入相同 K、nonce、AAD 与标签解密,任何不一致都应失败,不能把 upda...
密码学 06:对称加密为什么不能单独认证消息
给订单 JSON 加密后,旁观者读不出数量。是否可以省去校验身份与完整性的步骤,让接收端解密后直接创建订单?不能。“看不懂密文”只涉及某种保密目标,并不自动保证来源可信、内容没被有意改变;同一个 AES 算法配上不同的模式,观察到的泄露和验证行为也不同。本章先看一个不能用于一般订单的加密模式,再用独立的玩具模型观察改动密文的可能性,最后明确为何 07 要转向认证加密。 谁持有密钥,接收端检查什么 对称加密的典型输入是一个共享密钥、明文字节,以及所选模式要求的初始化输入;输出是密文字节。服务端拿到解密密钥后可以恢复明文。如果双方共享同一密钥,不看额外凭据,服务端通常也有能力制造看似由发送端生成的密文;这不是“只有浏览器才能签名”的公开可验证授权。 12345发送端:共享 K + 明文 + 模式参数 --> 密文 --不可信通道--> 接收端 | 共享 K 解密 v ...
密码学 05:随机数、salt、IV 与 nonce 各要求什么
订单服务给每个请求发一个 ID,钱包签交易时也会看到 nonce;密码数据库给每个用户密码加 salt,加密接口还让调用方传 IV 或 nonce。于是一个诱人的错误设计出现了:所有带这类名字的字段,都用一次 random() 生成,反正“随机就安全”。这会忽略两个相反的要求:有的值必须令攻击者难以预测,有的值最关键的是不能在同一作用域内重复;有些值甚至可以是单调计数器。 本篇选择四种具体场景,不教自己造随机数发生器。先看谁产生、谁保存、是否公开,再看如果重新使用一个值会发生什么。 12345678系统随机源 ----> 生成密钥 / 临时秘密 ----------> 只给有权持有者 \--> 密码 salt --------------------> 与密码哈希一起公开保存密钥 K + 每密钥唯一的 AEAD nonce + 明文 -> 密文/标签 ↑ 重启、并发、多副本都必须避免加密时重复ECDSA 私钥 + 消息 + 签名内部 k ----------> 签名(k 失守可泄露私钥)业务身份 + 请求...
密码学 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浏览器 ...















