ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

DNS 劫持检测接口接入教程:domain 参数、请求示例与风险分级解读

DNS 劫持检测接口接入教程:domain 参数、请求示例与风险分级解读

排查“域名解析异常”“被运营商插广告”这类问题时,光查一条 DNS 记录不够,需要对比多个解析源的返回结果。DNS 劫持检测接口通过多家公共 DoH 并发查询同一域名并比对结果,给出风险分级。本文从第一次调用讲起。

一、适用场景

  • 解析异常排查:用户反馈域名打不开,判断是本地污染还是服务端问题。
  • 安全巡检:对关键域名做定期检测,发现解析被篡改的迹象。
  • CDN 与 GeoDNS 核查:查看是否存在异常地域分流。
  • 终端 hosts 排查:对比公共 DoH 结果,辅助判断本机解析是否被改。

需要明确:接口给出的是“基于多源对比的风险判断”,不是对劫持行为的取证结论,业务上应作为排查线索而非最终裁决。

二、接口能力边界

项目说明
请求方法GET
参数domain
查询源4 家公共 DoH 并发查询
记录类型A + AAAA 同时查询(IPv4 / IPv6)
附加信息CNAME 链路、TTL 汇总
风险分级safe / safe_with_geodns / suspicious / hijack_likely / unknown
输入兼容domain、https://domain、domain:port、domain/path 自动剥离

参与对比的 DoH 服务器:

来源说明
AliDNS阿里云,中国
DNSPod腾讯,中国
360 安全 DNS中国
Cloudflare美国,在中国大陆服务器侧可能超时,会被标记为 timeout

关键点:Cloudflare 出现timeout是已知现象,单看这一项超时不能直接判定为风险,要结合其余三家的结果与整体分级来看。

三、请求参数与鉴权

参数只有一个:

参数说明
domain待检测域名,支持多种输入格式,自动剥离协议、端口与路径

鉴权方式与请求头字段,素材未给出,以文档为准。这是GET请求,参数放在查询字符串中,接入前请确认是否需要携带 Key。

四、最小请求示例

下面是一个可复制的 curl 模板:

curl -X GET 'https://v1.apizero.cn/api/dns-check?domain=example.com'

带协议头的输入也能直接传,接口会自动剥离:

curl -X GET 'https://v1.apizero.cn/api/dns-check?domain=https://example.com'

对应的 Python 调用示例:

import requests def check_dns_hijack(domain: str, api_key: str | None = None) -> dict: headers = {} if api_key: headers["Authorization"] = f"Bearer {api_key}" resp = requests.get( "https://v1.apizero.cn/api/dns-check", params={"domain": domain}, headers=headers, timeout=20, # 多源并发查询,超时给宽一些 ) resp.raise_for_status() return resp.json()

超时建议比普通查询放宽,因为接口内部要并发等待多家 DoH 返回。

五、返回值解读

风险分级是核心结果,五档含义如下(具体判定规则以文档为准):

分级含义理解
safe多家结果一致,未发现异常
safe_with_geodns结果差异可由正常 GeoDNS 分流解释
suspicious存在可疑差异,需人工复核
hijack_likely多源结果显著不一致,疑似劫持
unknown数据不足或查询源多数失败,无法判断

除分级外,还应关注 A/AAAA 记录、CNAME 链路与 TTL 汇总。CNAME 链路能看出是否经过 CDN 跳转,TTL 能辅助判断缓存影响。

一个防御式解析示例(字段名请按文档实际返回替换):

LEVELS = {"safe", "safe_with_geodns", "suspicious", "hijack_likely", "unknown"} def parse_check(payload: dict) -> dict: level = payload.get("level") or payload.get("risk") # 字段名以文档为准 if level not in LEVELS: level = "unknown" sources = payload.get("sources") or {} timeouts = [name for name, info in sources.items() if isinstance(info, dict) and info.get("status") == "timeout"] return { "level": level, "timeout_sources": timeouts, "need_manual_review": level in ("suspicious", "hijack_likely"), }

处理逻辑建议:suspicious与hijack_likely进入人工复核队列;unknown先重试一次再判定;safe_with_geodns不必告警。

六、常见错误与排查

现象可能原因排查方向
参数错误domain为空或格式非法传标准域名,其余格式由接口剥离
超时查询源响应慢,Cloudflare 侧可能超时放宽超时,看是否为单源超时
结果为 unknown多数查询源失败或数据不足稍后重试,检查域名是否可解析
分级与预期不符GeoDNS 正常分流被误判结合 CNAME 与 A 记录人工复核
鉴权失败需要 Key 但未携带以文档为准补充请求头

注意:单个来源timeout属于正常现象,不要把它当成劫持证据,也不要在这种情况下直接告警。

七、工程化注意事项

  • 超时设置:多源并发查询耗时更长,客户端超时应显著大于单次 DNS 查询。
  • 分级映射:本地维护五级枚举,未知值统一归入unknown,避免展示层崩溃。
  • 告警策略:只对hijack_likely直接告警,suspicious走人工复核,unknown重试后再定。
  • 来源记录:保存各 DoH 的返回与超时情况,便于复盘误判。
  • 巡检频率:域名安全巡检按小时或按天做即可,不宜高频调用。
  • 结果展示:把 CNAME 链路和 TTL 一并展示给排查人员,比只给一个分级更有用。

参考文档

  • 接口文档

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进