Perl 02:上下文来自调用位置,不来自变量名
@services 被赋值时,右边收到列表上下文;$service 被赋值时,右边收到标量上下文。表达式自己并不携带一个永恒的“返回类型”。perldata 的 Context 一节特别用 <STDIN> 举例:传给 int 与传给 sort 的读入范围可以不同。对日志解析来说,这意味着“读一条”还是“读到 EOF”,不能由变量名字猜出来,必须看调用位置和具体运算符文档。 实验 examples/perl-language/experiments/02-context.pl 定义 describe_context():列表位置给两个服务名,标量位置给 one,丢弃值的位置返回空列表。运行后 STDOUT 前两行是 list=api|worker 和 scalar=one;另有 scalar-array=2、scalar-reverse=ba、list-reverse=b|a,STDERR 空、退出码 0。脚本确实调用了 void 位置,却没把无输出伪装成对其返回值的观测:那里被丢弃的值本就不会打印。wantarray 能区分 void;perldata 同时指出,对只...
Perl 01:作用域是解析边界,warnings 不是校验器
同一行日志里有服务名 api,解析函数里也可能临时使用 service。如果读取、解析、筛选都读写同一个包变量,后一次处理就可能改变前一次的状态。my 给每个词法作用域一个局部名字,use strict; use warnings; 则让未声明名字早暴露,并显示可疑转换;它们并不自动保证输入符合日志格式。 examples/perl-language/experiments/01-scope.pl 在外层声明 $service = 'api',内层再声明一个 $service = 'worker'。运行 perl examples/perl-language/experiments/01-scope.pl,STDOUT 为 inside=worker、outside=api 两行,STDERR 空,退出码 0。输出证明了这两个声明的遮蔽范围;它没有证明任何输入已经通过字段校验。正式工具的 LogTool::read_records 让文件句柄、行号、@fields 和输出记录局限在一次调用中,返回数组引用,不把当前行留在包级变量里。 反例运行 perl -we 'use stri...
Perl 00:先冻结解释器,再约定合成日志
两台机器都说“运行 Perl”,却可能使用不同 patch、不同的系统补丁和不同的模块版本。把一段脚本跑通,不等于复现了同一条语言规则。这一篇先钉住运行对象和输入输出字节;后面每个上下文或编码结论,都要在这个坐标下解释。 课程导航 按原定主线 00–39、选修 E01–E08 的主题组织学习路径。2026-10-06 的实际文章编号与原定课程编号有差异,导航列出主题映射及补充专题。文章数量、实验通过和原定覆盖分别核对,不能互相代替。 先区分四种版本 2026-10-06 UTC,在此容器执行 perl -v 得到 5.38.2;readlink -f /usr/bin/perl 指回 /usr/bin/perl,它的 SHA-256 为 fa36b75b21e94f424b61aa058b743cc62c94e7fa45a085fdeffb2bae687ed925。系统包 perl-base 版本是 5.38.2-3.2ubuntu0.4。完整 perl -V 原文收在 examples/perl-language/evidence/perl-V.txt;该文件的 SHA-256...
密码学 17:恢复连接、PSK 与 0-RTT 省掉了什么
一次合成订单访问结束,浏览器很快又连同一服务。TLS 1.3 可以利用旧连接留下的恢复材料,避免每次都完整重复此前的证书握手;更激进的做法是在服务端回应本次 ClientHello 前发送应用字节。查询商品和创建付款订单不能因为都叫“HTTPS 请求”就无差别提前。恢复、PSK-only、PSK-DHE、0-RTT指向不同决策:旧秘密是否被复用,本次是否加入新的临时 DH,以及握手确认之前是否让业务处理早期消息。 本篇实测了一次 TLS 1.3 普通握手,再借内存里的票据成功恢复一次 PSK-DHE 握手;环境没有真实运行 0-RTT 或 PSK-only。没跑过的分支只据 RFC 9846 讨论规则,仍留作 NOT_RUN。 票据把上一轮带到了下一轮 第一次证书认证路径已把预期 orders.test 的身份与本次握手绑定。握手后,服务端可发 NewSessionTicket;客户端保存与票据相关的恢复材料。下一次 ClientHello 提供相应的 pre_shared_key 身份,服务端若接受它,双方才可用先前握手派生的 PSK 引导新的密钥协商。这里的预共享密钥并非把原...
密码学 16:TLS 怎样保护每条记录
15 的握手证实客户端连到了预期的 TLS 服务端并建立共享秘密。接下来的合成订单不是把证书公钥放在每一条 HTTP 请求前,重新签一遍;两端改用各自方向的应用流量密钥,给一条条 TLS 记录做认证加密。为什么记录还需要序号?如果攻击者复制刚才发送的那一条加密记录,再发一次,接收端能否分辨它不是下一条?TLS 记录的复制重放与有权客户端重新提交一笔相同订单是两个不同问题。 本篇实际在 Python/OpenSSL 的 TLS 1.3 MemoryBIO 端点之间截取一份应用数据的受保护记录,分别试正常接收、对端回发相同明文、重复投递同一加密记录以及翻转一位加密记录。它是成熟 TLS 库在内存里的真实协议组件实验;并没有提取/公开会话秘密、手写 TLS 记录格式或自行实现 AES-GCM。 从一份秘密派生多条路 10 的密钥交换得到共同输入,11 的 HKDF 说明可以按上下文派生分用途输出。TLS 1.3 按其自己的派生树取得客户端方向与服务器方向的应用流量秘密,进一步计算各方向的记录密钥和静态 IV。即便两个方向传送一模一样的订单字节,参与认证加密的密钥、记录序号以及上下文也不...
密码学 15:TLS 1.3 握手逐步解释
浏览器连接 orders.test,握手里既出现两端的临时 key share,又出现服务器证书、CertificateVerify 和 Finished。如果证书已经写了公钥和名称,为什么还要后两步?如果 CertificateVerify 是签名,为什么还要 Finished?把这些消息统称为“TLS 验证一下”会掩盖三种攻击入口:服务名冒充、这次 key share 被替换、双方对握手与密钥的理解不一致。本文沿一次证书认证的 TLS 1.3 握手,给每个对象写清谁持有秘密、输入输出和删掉之后的反例。 规范基线是 RFC 9846:它在 2026 年取代 RFC 8446,并重排了部分章节。真实实验的 OpenSSL 3.0.13 发布早于它,所以本机协商到 TLS 1.3、出现规范要求的消息,不等于宣称已通过 RFC 9846 每项修订的实现符合性测试。 第一步:协商需要什么而不是发一把密钥 客户端自己预期访问 orders.test,并持有一次性测试 CA 的可信证书;服务器有叶子证书及对应私钥。两端为本次会话准备临时交换秘密与相应公开值,ClientHello、Serv...
密码学 14:证书为什么能绑定服务名与公钥
有人给浏览器发来一把公钥,说“我是 orders.test”。浏览器用它验证同一人送来的签名,结果当然可能成功。13 已经说明:**签名能证明私钥持有人参与了那次操作,却不能只靠自报的公钥确定持有人叫什么。**网站身份校验必须从浏览器自身预期访问的名称和事先配置的可信根出发,而不能让连接对面临时决定信任起点。 本篇用临时合成 CA 给 orders.test 签发一张测试证书,只在本机回环地址启动 TLS 1.3 服务端。客户端明确加载这一张 CA,匹配 DNS 名称后提交一笔合成订单;另试“名称不符”和“客户端没有这张 CA”两个失败路径。这里跑到了真实 OpenSSL/Python TLS 端点,但不是浏览器实验,更不把 CA 装进系统信任库。 验证者有一个先于网络对话的预期 TLS 连接发往哪个 IP 与客户端要认证哪个服务是两个不同输入。测试里套接字连的是 127.0.0.1 的随机端口,但客户端指定的服务名称是 orders.test。证书的 subjectAltName(SAN)包含 DNS 名称 orders.test;用于验证这张证书的测试 CA 只交给本次客户端...
密码学 13:正确公钥从哪里来
浏览器连上一个声称是 orders.test 的服务,服务把自己的公钥和对某段数据的签名一起发来。浏览器用收到的公钥验证,返回真。现在可以提交订单吗?还不可以:攻击者完全可以自己生成公私钥,给自己编的一段消息签名,再附上自己的公钥。算法做对了,身份却是假的。10 篇说明 DH 可以和中间人分别建立共同秘密;12 篇说明签名可以被正确公钥验证。要把两种能力组合为“与预期服务安全通信”,先得知道应当信任哪个公钥,并确认它签的是这一次、这个角色、这段通信。 本篇用事先固定的合成公钥模拟“浏览器已有可信起点”,再演示恶意自签、错误服务名、换 key share 与重放。固定公钥只是教学前提,不是现实世界的信任来源;14 的证书链才真正回答 HTTPS 的服务名与公钥如何绑定。 相同签名算法,两种截然不同的输入 客户端看到“消息 + 签名 + 公钥”时,至少要决定三个独立输入:希望连接的服务名称,从可信配置、证书链或别的认证制度拿到的预期公钥或公钥约束,以及要验证的确切消息字节。如果把公钥信任和消息验证都留给发件者随意指定,验证会变成自我背书。 12345678本地预期服务 orders....
密码学 12:数字签名验证了什么
钱包要给节点一个授权证据:节点必须能独立检查“这组明确字节经某个有权的私钥持有人签署”,却不应该因此获得生成新授权的能力。04 篇的 HMAC 做不到后一条,因为接收者也持有同一把密钥。数字签名提供另一种分工:私钥参与生成签名,公钥参与验证签名。但是验签函数返回 true 仍有至少两个问题悬而未决:这个公钥属于谁?这段被签的字节是否就是需要授权的那件事? 这篇用成熟库分别运行 RSA-PSS、P-256 上的 ECDSA 与 Ed25519 的正负验签,严格区分“原语实验通过”和“业务或链上的授权已经生效”。 三个输入,不是一句“签过了” 发送者在既定算法与编码约定下对确定字节生成签名;验证者必须同时拥有消息字节、签名和可信来源的正确公钥,还要知道怎样解码签名与选择算法参数。对恶意者自造密钥并签署的消息,verify(恶意者的公钥, 消息, 签名) 也能返回真,不能因此推断发件人是合法钱包主人或预期网站。 1234567甲:私钥(不得外发) + 确定的消息字节 -> 签名 | | ...
密码学 11:一个秘密为什么派生多把密钥
10 篇里,两个临时密钥交换参与者算到同一份秘密。接下来的订单请求和服务端响应,能不能把这串值直接拿来当一把万能密钥,既负责客户端发送又负责服务端发送,未来连请求认证也复用?不该这样做。不同方向、阶段与用途需要隔离:一边的密钥、nonce 进度或使用权限变化,不应无端变成另一边的隐性输入。派生密钥是从一份已经足够不可猜测的输入得到明确用途的多份输出,不是把弱密码凭空升级成强秘密。 这篇用 HKDF 的标准向量验证算法输出,再对一份进程内临时秘密分别派生客户端订单、服务端订单和 RPC 用途密钥。不打印实际临时秘密与派生密钥;公开的十六进制输出仅限 RFC 官方测试向量。 提取与扩展各自输入什么 HKDF 使用 HMAC 构成两步:先对输入密钥材料 IKM 与 salt 进行提取,得到伪随机密钥 PRK;再以 PRK、表示上下文和用途的 info、指定输出长度 L 做扩展,得到所需密钥材料。接口可以简写如下,但真实调用一定要给出字节级参数: 123456789101112 已有秘密 IKM + salt | v HKDF-Ex...










