01 · 数据源全景与选型
CT监测的数据源分三类:日志直连(完整、实时、工程成本高)、聚合服务(即用、覆盖全量日志、受服务条款约束)、派生情报(开源社区与商业监测服务的告警流)。组织应按数据主权、实时性与成本三要素组合选型。
| 数据源 | 机制 | 实时性 | 适用场景 |
|---|---|---|---|
| CT日志直连API | 对Chrome/Apple清单内全部日志执行get-sth/get-entries增量拉取(RFC 6962 §4) | 秒级—分钟级 | 大规模自建平台、学术研究(数据完整可控) |
| certstream(Calidog) | WebSocket推送全量CT证书记录的社区流服务 | 近实时 | 快速搭建检测原型、中小规模告警管道 |
| crt.sh | Sectigo运营的公共查询(Web+JSON API),覆盖全量日志 | 分钟级延迟 | 品牌自查、历史检索、人工研判(不适合高频轮询) |
| Censys | 互联网空间搜索引擎,证书数据集(parsed.names等字段可检索) | 小时级快照 | 生态测量、历史回溯、资产盘点 |
| Google证书透明度报告 | Google官方CT生态数据与证书搜索界面 | 小时级 | CA合规核验、辅助研判 |
| SSLMate Cert Spotter | 商业/开源CT监测服务,按词根订阅并推送新增证书告警 | 近实时 | 组织级品牌监测的零开发起步方案 |
覆盖性原则:没有任何单一数据源可宣称"覆盖全网签发"——CA只需向政策要求的日志组合提交证书,而各监测服务的日志覆盖范围、同步时延存在差异。关键结论须以多源交叉验证(≥2个独立来源)为准(对应第1页KQ-1)。
02 · 直连接入架构:多日志并行Tail
2.1 标准接入流水线
2.2 关键工程机制
- 断点续传:以"日志ID+最后消费索引"为游标持久化;进程重启后从游标续拉;对日志侧偶发5xx与超时实现指数退避重试。
- 礼貌与速率:各日志运营方在清单中声明查询服务能力;接入方应遵守合理并发与请求间隔,避免对日志造成压力(多数日志由运营方无偿提供,滥用将导致IP被限流)。
- 预证书/正式证书归并:同一证书通常先以precert_entry、后以x509_entry出现(见第2页);归并键=证书序列号+SPKI指纹。归并后只保留一条业务记录(保留两个日志来源作为溯源字段),避免告警翻倍;注意部分CA只提交其一通道的例外情况。
- 日志健康监控:监控每日志tree_size增长率与STH时间——增长率突降、STH停发是日志冻结/故障/退役的前兆,触发告警并启动清单重同步。
- 反压与降级:消费速度低于生产速度时,优先保障高危品牌通道,低优先级的生态测量通道可降级为抽样。
// 伪代码:基于 certificate-transparency-go 的单日志增量消费(示意,非生产代码)
for {
sth := log.GetSTH() // 获取最新树头
tip := sth.TreeSize
for cur := cursor[logID]; cur < tip; {
batch := log.GetEntries(cur, min(cur+256, tip)-1) // RFC 6962 get-entries
for _, e := range batch {
leaf := ParseLeaf(e.Leaf) // 兼容 v1/v2 叶节点编码
cert := ChainToCert(e.Chain) // 构造证书链并提取字段
dedupKey := cert.Serial.String() + "|" + SPKIFingerprint(cert)
if seen.Add(dedupKey) { // 序列号+SPKI 去重
emit(Normalize(cert)) // 域名IDNA规范化、字段标准化
}
}
cursor[logID] = min(cur+256, tip)
persistCursor(cursor) // 断点持久化
}
sleep(pollInterval) // 指数退避/固定间隔
}工程教训:①不要把crt.sh当作实时流源轮询——它是公共服务,高频请求违反其使用条款且延迟不可控;②预证书归并失败是最常见的"告警翻倍"根因;③日志清单变更(新日志上线)若无自动发现机制,将造成结构性盲区——清单同步必须自动化。
03 · 解析与标准化规范
3.1 证书字段提取(监测视角最小集)
| 字段 | 来源 | 监测用途 |
|---|---|---|
| SAN列表 / CN | 证书主体扩展(subjectAltName、commonName) | 仿冒检测主输入;注意CN字段已多年不被浏览器信任(2017年起Chrome仅认SAN),但仍是情报特征 |
| 序列号 | tbsCertificate.serialNumber | 预证书/正式证书归并键;CA内部追踪 |
| SPKI指纹 | 公钥比特的SHA-256 | 归并键;同私钥多证书/多域名基础设施聚类 |
| 签发者 / 有效期 | issuer、notBefore/notAfter | CA分布统计;钓鱼证书生命周期画像 |
| SCT列表(嵌入式) | 扩展OID 1.3.6.1.4.1.11129.2.4.2 | 反查证书经由哪些日志/何种组合递交——辅助识别CA行为模式 |
3.2 域名规范化(误报控制的第一道闸)
- IDNA处理:对xn--前缀域名执行Punycode解码后比较(RFC 3492),避免同形异义域名(详见第4页)因编码差异漏检;同时保留原始形态用于展示与溯源。
- 大小写与尾部点:域名比较一律小写、去除尾部根点;通配符域名(*.example.com)在匹配品牌词根时按"标签级"处理。
- 标签切分:按点切分标签序列——相似度计算在"有效标签"(去除通配符*)上进行,避免TLD干扰。
- 时间戳规范化:日志时间戳为毫秒级UTC;与域名注册时间(RDAP/WHOIS)比对时统一时区,计算"注册→签发"间隔。
04 · 存储、索引与数据量级
- 量级估算:全球公网证书日增量为百万级(Let's Encrypt官方统计页显示其日签发量长期为百万量级,全生态叠加后更高);单条标准化记录(域名+元数据)约数百字节,日增存储在GB级以下——存储不是瓶颈,索引与查询模式才是。
- 热路径:消息队列(Kafka等)承接实时流;检测引擎消费热数据,状态保存在内存/Redis(品牌词根索引、最近N天域名布隆过滤器用于去重告警)。
- 冷存储:全量记录入列式存储或对象存储(Parquet+对象存储是生态测量场景的高性价比组合),保留期按合规要求设定(第8页)。
- 核心索引:①域名倒排(eTLD+1聚合);②品牌词根索引(trie/AC自动机,支持前缀与后缀匹配);③SPKI指纹索引(基础设施聚类);④签发CA+时间窗口索引(生态测量)。
05 · 检测执行与告警链路
| 环节 | 设计要点 | 失败模式与对策 |
|---|---|---|
| 匹配执行 | AC自动机执行词根匹配;命中后进入相似度评分管道(第4页算法) | 词根膨胀导致性能下降→词根分级加载,仅活跃词根常驻内存 |
| 去重与聚合 | 同证书归并、同基础设施(SPKI/IP)聚合为单一事件,附受影响域名列表 | 批量钓鱼导致告警风暴→按基础设施聚类合并为一条"批量事件" |
| 分级路由 | 高危→值班Webhook/电话;中危→工单队列;低危→日报汇总 | 通道疲劳→分级阈值季度评审,结合误报反馈调整 |
| 闭环反馈 | 研判结果(真仿冒/误报/合法)回流,更新白名单与权重 | 白名单腐化→定期复审,白名单条目设有效期与责任人 |
时延预算(目标基线,方法建议):日志并入→接入管道≤5分钟;解析归并≤1分钟;检测评分≤1秒;告警触达≤1分钟。端到端高危告警目标为签发后15分钟内送达值班人员(对应第1页时效目标)。
06 · 开源工具链选型对照
| 工具/组件 | 定位 | 选型建议 |
|---|---|---|
| certificate-transparency-go | CT协议官方Go库:类型定义、日志客户端、证明验证、数据扫描工具 | 自建接入/审计工具的事实标准基础库 |
| certstream(Calidog) | WebSocket全量CT流推送服务与客户端SDK | 原型验证与小规模告警;生产使用需评估服务可用性条款 |
| Trillian | 透明数据存储框架(Merkle树+签名+API) | 自建日志/审计节点的底座(非监测消费方必需) |
| crt.sh | 公共检索与JSON API | 人工研判与低频品牌自查;勿作高频流源 |
| Censys | 证书与主机数据搜索、API与数据集导出 | 生态测量与历史回溯;注意配额与引用条款 |
| SSLMate Cert Spotter | CT监测告警服务(商业+开源组件) | 零开发起步的组织监测方案;自建时可参考其开源实现 |
// 伪代码:certstream 消费端(Python 示意,依赖 websocket-client,非生产代码)
import json, websocket
def on_message(ws, msg):
rec = json.loads(msg)
if rec.get("message_type") != "certificate_update":
return
leaf = rec["data"]["leaf_cert"]
for name in leaf.get("all_domains", []):
normalized = idna_normalize(name) # Punycode解码/小写/去尾点
score, brand = detect_engine.score(normalized) # 第4页检测引擎
if score >= HIGH_RISK:
alert_router.dispatch_high(brand, normalized, leaf)
ws = websocket.WebSocketApp("wss://certstream.calidog.io/",
on_message=on_message)
ws.run_forever()本节参考来源(本页)
以下均为真实可查的权威来源(IETF RFC官方档案、浏览器/日志运营方官方文档、开源项目、学术会议论文),用于支撑本页事实性表述。
RFC 6962第4节(日志只读HTTP API:get-sth/get-entries/get-proof-by-hash等):rfc-editor.org/rfc/rfc6962;RFC 9162(v2 API风格):rfc-editor.org/rfc/rfc9162。
certificate-transparency-go官方库:github.com/google/certificate-transparency-go;Trillian:github.com/google/trillian。
certstream(Calidog):certstream.calidog.io;crt.sh:crt.sh(查询参数与速率约定见其站点说明)。
Censys证书搜索:search.censys.io;SSLMate Cert Spotter:sslmate.com/certspotter。
Chrome CT日志清单(接入范围基准):gstatic.com/ct/log_list/v3/log_list.json;Google证书透明度报告:transparencyreport.google.com。
Let's Encrypt官方签发统计(日签发量级参考):letsencrypt.org/stats。
RFC 3492(Punycode/IDNA编码基础):rfc-editor.org/rfc/rfc3492。