如何保存带页面源码SSL签名的HTML页面实现快照防篡改?
先纠正一个认知偏差
你对HTTPS/SSL签名的作用理解有问题:TLS记录层的逐包签名是会话绑定、临时生成的,签名校验用的密钥是你和服务器握手时协商的临时会话密钥,连接断开后就失效了。你就算把传输过程中的所有加密包和对应签名全存下来,第三方没有当时的会话密钥根本没法校验,而且TLS本身的设计目标只是防传输链路中的实时篡改,根本不是用来做长期内容存证的,走这条路完全行不通。
可落地的实现方向(适配PHP技术栈,按实现难度从低到高排)
- 基础方案:基于证书链的内容快照存证(最快能跑通)
不用抓传输层的包,你用PHP发起请求拉取网页原始内容时,同步拿到目标站点的TLS证书链、CA对证书的签名——这部分是CA根证书背书的,长期可验证。快照包固定包含这几部分内容:- 拉取到的网页原始字节流(不要做任何DOM解析、格式修正,避免改到原始内容)
- 抓取时间戳、目标域名
- 服务器返回的完整TLS证书链、证书序列号
- 原始内容的SHA-256哈希值
校验逻辑也很简单:先验证快照内的证书链是合法CA签发、域名匹配、抓取时间在证书有效期内,再重新计算网页内容的哈希和快照内存储的哈希比对,就能确认内容有没有被篡改。
PHP实现直接用内置扩展就行:cURL开CURLOPT_CERTINFO参数就能在请求时自动拿到完整证书链,原始内容直接通过curl_exec()拿原始返回值,哈希计算用内置的hash('sha256', $rawContent)就够,快照打包直接用Zip扩展把所有文件封成单个文件,你可以再加个自己服务端的私钥签名,防止快照包本身被伪造。
- 增强方案:补充TLS握手层的可验证证据
如果你想存传输层的可公开验证证据,别存应用数据的加密包(TLS1.3默认开前向保密,就算你存了会话密钥后面也解不开,毫无意义),只需要存TLS握手阶段的Certificate、ServerKeyExchange消息——这部分是服务器用证书对应的私钥签的,任何人拿到证书公钥都能验证这部分数据确实是目标服务器发的。
纯PHP实现抓握手包比较麻烦,你可以在服务器上用tcpdump配置规则只抓目标站点443端口的握手阶段前几个包,PHP通过exec()调用抓包命令,拿到握手包后立刻停止抓包,把抓到的pcap片段和前面的基础快照打包到一起就行。 - 可选升级:加RFC3161可信时间戳
如果你需要快照具备更高的可信度(比如存证场景),可以把快照内容的哈希提交给公开的RFC3161时间戳服务拿到权威时间戳签名,一起打进快照包,就能证明对应内容在抓取时间点确实存在、未被篡改。PHP有现成的RFC3161客户端类可以直接用,不用自己手写签名逻辑。
避坑提醒
- 绝对不要存加密后的应用数据段,不管你觉得当时的签名多完整,脱离了实时会话根本没法校验,纯浪费存储。
- 拉取网页内容的时候一定要拿原始字节流,不要用任何DOM解析库、内容过滤插件处理后再存,哪怕是自动补全HTML标签、转义特殊字符这种小改动,都会导致内容哈希变化,后续校验直接失效。
- 快照里必须存抓取当时拿到的完整证书链,不要只记域名,证书会过期、会被吊销,你不存当时的证书,后面根本没法证明你抓取时站点用的是合法签发的证书。
内容的提问来源于stack exchange,提问作者user2594042
相关产品推荐
相关产品推荐

