跳到主要内容

附录 A. DMARC XML Schema

本附录给出 DMARC aggregate report 的 XML Schema. 该 schema 定义报告根元素, 报告元数据, 发布策略, 每条 record 的 row, identifiers, auth_results, policy_evaluated, reason 和 extension 等结构. 它还定义了 alignment mode, disposition, discovery method, DKIM/SPF result, IP address, header comment 和 XML extension 的类型约束.

下方 XML Schema 是规范性机器可读内容, 因此保留 xs:schema, namespace URI, element/type 名称, enumeration 值和 XML comment. 中文导读用于说明阅读路径: ReportMetadataType 描述报告生成方和报告周期; PolicyPublishedType 描述 DMARC Policy Record 中发布的策略; record 相关类型描述接收方观察到的消息来源, 标识符和认证结果; extension 相关定义允许未来文档在指定位置扩展报告结构.

实现者在验证 report 时, 应将此 schema 与正文中对元素顺序, 必选/可选元素和扩展点的说明结合使用. 本附录中的英文 XML comment 是 schema 的一部分, 保持英文可帮助工具链和测试向量逐字节复用.

从结构上看, 该 schema 首先定义通用字符串和时间范围类型, 随后定义报告元数据, 策略发布信息, 逐条记录的认证结果, 以及错误和扩展信息. AlignmentTypers 表示 relaxed 与 strict; DispositionTypeActionDispositionType 描述发布策略和实际处置; DiscoveryType 区分 psltreewalk 两种策略发现方式. 这些枚举值是协议互操作所需的精确字符串, 不应翻译.

ReportMetadataType 中的 org_name, email, report_iddate_range 是报告来源和报告周期的核心字段. PolicyPublishedType 描述报告生成时看到的 DMARC Policy Record, 包括域名, p, sp, np, testing, alignment mode 和 discovery method. record 相关类型把 source IP, message count, policy evaluation, identifiers 和 authentication results 组织在一起, 使域所有者能够按来源地址和认证机制分析通过或失败情况.

XML schema 中的 extension 点用于未来扩展, 但扩展元素必须位于 schema 允许的位置并使用 namespace. 因此, 接收方实现需要在严格验证基础结构的同时, 对未知扩展保持可忽略性. 这也是本文档正文中可扩展报告模型的机器可读表达.

从数据流角度看, 一个聚合报告通常由接收方或报告服务在一个报告周期结束后生成. date_range 给出该周期, policy_published 记录报告时观察到的发送域策略, 每个 record 则聚合具有相同来源 IP, 评估结果和标识符组合的一组消息. count 不是逐封邮件清单, 而是对同类观测结果的计数; 因此消费方在统计时应按 record 聚合维度理解它, 而不是假定每个 record 只代表一封消息.

policy_evaluated 中的 dkimspf 表示与 DMARC 对齐后的结果, 而 auth_results 中的 DKIM/SPF 条目保留底层认证机制自身的结果. 这一区分很重要: 底层 DKIM 或 SPF 可以通过, 但如果身份未对齐, DMARC 结果仍可能失败. reason 元素用于解释本地策略, 邮件列表, 测试模式或可信转发方等因素如何影响最终处置, 因而它是域所有者分析误报和策略例外的重要线索.

该 schema 也明确了报告中哪些字段是必需的, 哪些字段可以省略. 例如, report_metadata, policy_published 和至少一个 record 是完整报告的核心结构; extra_contact_info, generator, fo, testing, human_result 和扩展元素则用于提供附加上下文. 接收方实现应该接受合法的可选字段缺失, 但不得忽略必需字段的缺失或枚举值越界. 发送方实现则应避免在自由文本字段中放入不必要的个人信息或内部拓扑细节.

安全和互操作层面, XML 解析器应采用适合不可信输入的配置, 包括限制实体展开, 控制文档大小, 并避免把扩展 namespace 的内容作为已知语义直接执行. 同时, schema validation 只能证明结构符合本附录定义, 不能证明报告内容真实可信. 报告接收方仍需结合传输安全, 报告来源认证和本地信任策略来判断数据是否可用于运维或策略决策.

   <?xml version="1.0"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="urn:ietf:params:xml:ns:dmarc-2.0"
xmlns="urn:ietf:params:xml:ns:dmarc-2.0"
elementFormDefault="qualified">

<!-- Elements with an optional "lang" attribute. -->
<xs:complexType name="langAttrString">
<xs:simpleContent>
<xs:extension base="xs:string">
<xs:attribute name="lang" type="xs:language"
use="optional" default="en"/>
</xs:extension>
</xs:simpleContent>
</xs:complexType>

<!-- The time range in UTC defining the reporting period of
this report, specified in seconds since epoch. -->
<xs:complexType name="DateRangeType">
<xs:all>
<xs:element name="begin" type="xs:integer"
minOccurs="1" maxOccurs="1"/>
<xs:element name="end" type="xs:integer"
minOccurs="1" maxOccurs="1"/>
</xs:all>
</xs:complexType>

<!-- Report generator metadata. -->
<xs:complexType name="ReportMetadataType">
<xs:all>
<!-- Reporting Organization -->
<xs:element name="org_name" type="xs:string"
minOccurs="1" maxOccurs="1"/>
<!-- Contact to use when contacting the Reporting Organization -->
<xs:element name="email" type="xs:string"
minOccurs="1" maxOccurs="1"/>
<!-- Additional contact details -->
<xs:element name="extra_contact_info" type="langAttrString"
minOccurs="0" maxOccurs="1"/>
<!-- Unique Report-ID -->
<xs:element name="report_id" type="xs:string"
minOccurs="1" maxOccurs="1"/>
<!-- Timestamps used when forming report data -->
<xs:element name="date_range" type="DateRangeType"
minOccurs="1" maxOccurs="1"/>
<!-- Optional error messages when processing DMARC policy -->
<xs:element name="error" type="langAttrString"
minOccurs="0" maxOccurs="1"/>
<!-- Optional information about the generating software -->
<xs:element name="generator" type="xs:string"
minOccurs="0" maxOccurs="1"/>
</xs:all>
</xs:complexType>

<!-- Alignment mode (relaxed or strict) for DKIM and SPF. -->
<xs:simpleType name="AlignmentType">
<xs:restriction base="xs:string">
<xs:enumeration value="r"/>
<xs:enumeration value="s"/>
</xs:restriction>
</xs:simpleType>

<!-- The policy actions specified by p, sp, and np in the
DMARC Policy Record. -->
<xs:simpleType name="DispositionType">
<xs:restriction base="xs:string">
<xs:enumeration value="none"/>
<xs:enumeration value="quarantine"/>
<xs:enumeration value="reject"/>
</xs:restriction>
</xs:simpleType>

<!-- The policy actions utilized on messages for this record. -->
<!--
"none": No action taken
"pass": No action, passing DMARC w/enforcing policy
"quarantine": Failed DMARC, message marked for quarantine
"reject": Failed DMARC, marked as reject
-->
<xs:simpleType name="ActionDispositionType">
<xs:restriction base="xs:string">
<xs:enumeration value="none"/>
<xs:enumeration value="pass"/>
<xs:enumeration value="quarantine"/>
<xs:enumeration value="reject"/>
</xs:restriction>
</xs:simpleType>

<!-- The method used to discover the DMARC Policy Record used during
evaluation. The available values are "psl" and "treewalk",
where "psl" is the method from RFC 7489 and "treewalk" is
described in RFC 9989. -->
<xs:simpleType name="DiscoveryType">
<xs:restriction base="xs:string">
<xs:enumeration value="psl"/>
<xs:enumeration value="treewalk"/>
</xs:restriction>
</xs:simpleType>

<!-- The published DMARC policy. Unspecified tags have their
default values. -->
<xs:complexType name="PolicyPublishedType">
<xs:all>
<!-- The domain at which the DMARC record was found. -->
<xs:element name="domain" type="xs:string"
minOccurs="1" maxOccurs="1"/>
<!-- The policy published for messages from: -->
<!-- * the domain. -->
<xs:element name="p" type="DispositionType"
minOccurs="1" maxOccurs="1"/>
<!-- * subdomains. -->
<xs:element name="sp" type="DispositionType"
minOccurs="0" maxOccurs="1"/>
<!-- * non-existent subdomains. -->
<xs:element name="np" type="DispositionType"
minOccurs="0" maxOccurs="1"/>
<!-- The DKIM alignment mode. -->
<xs:element name="adkim" type="AlignmentType"
minOccurs="0" maxOccurs="1"/>
<!-- The SPF alignment mode. -->
<xs:element name="aspf" type="AlignmentType"
minOccurs="0" maxOccurs="1"/>
<!-- Method used to find/obtain DMARC policy. -->
<xs:element name="discovery_method" type="DiscoveryType"
minOccurs="0" maxOccurs="1"/>
<!-- Failure reporting options in effect. -->
<xs:element name="fo" type="xs:string"
minOccurs="0" maxOccurs="1"/>
<!-- Whether testing mode was declared in the DMARC Record. -->
<xs:element name="testing" type="TestingType"
minOccurs="0" maxOccurs="1"/>
</xs:all>
</xs:complexType>

<!-- Values for Testing mode attached to policy. -->
<xs:simpleType name="TestingType">
<xs:restriction base="xs:string">
<xs:enumeration value="n"/>
<xs:enumeration value="y"/>
</xs:restriction>
</xs:simpleType>

<!-- The DMARC-aligned authentication result. -->
<xs:simpleType name="DMARCResultType">
<xs:restriction base="xs:string">
<xs:enumeration value="pass"/>
<xs:enumeration value="fail"/>
</xs:restriction>
</xs:simpleType>

<!-- Reasons that may affect DMARC disposition or execution. -->
<xs:simpleType name="PolicyOverrideType">
<xs:restriction base="xs:string">
<xs:enumeration value="local_policy"/>
<xs:enumeration value="mailing_list"/>
<xs:enumeration value="other"/>
<xs:enumeration value="policy_test_mode"/>
<xs:enumeration value="trusted_forwarder"/>
</xs:restriction>
</xs:simpleType>

<!-- Override reason consists of pre-defined override type and
free-text comment. -->
<xs:complexType name="PolicyOverrideReason">
<xs:all>
<xs:element name="type" type="PolicyOverrideType"
minOccurs="1" maxOccurs="1"/>
<xs:element name="comment" type="langAttrString"
minOccurs="0" maxOccurs="1"/>
</xs:all>
</xs:complexType>

<!-- Taking into account everything else in the record,
the results of applying DMARC. If alignment fails
and the policy applied does not match the domain's
configured policy, the reason element MUST be specified. -->
<xs:complexType name="PolicyEvaluatedType">
<xs:sequence>
<xs:element name="disposition" type="ActionDispositionType"
minOccurs="1" maxOccurs="1"/>
<xs:element name="dkim" type="DMARCResultType"
minOccurs="1" maxOccurs="1"/>
<xs:element name="spf" type="DMARCResultType"
minOccurs="1" maxOccurs="1"/>
<xs:element name="reason" type="PolicyOverrideReason"
minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="RowType">
<xs:all>
<!-- The connecting IP. IPv4address or IPv6address
as defined in RFC 3986, Section 3.2.2. -->
<xs:element name="source_ip" type="xs:string"
minOccurs="1" maxOccurs="1"/>
<!-- The number of messages for which the
PolicyEvaluatedType was applied. -->
<xs:element name="count" type="xs:integer"
minOccurs="1" maxOccurs="1"/>
<!-- The DMARC disposition applied to matching messages. -->
<xs:element name="policy_evaluated" type="PolicyEvaluatedType"
minOccurs="1" maxOccurs="1"/>
</xs:all>
</xs:complexType>

<xs:complexType name="IdentifierType">
<xs:all>
<!-- The RFC5322.From domain. -->
<xs:element name="header_from" type="xs:string"
minOccurs="1" maxOccurs="1"/>
<!-- The RFC5321.MailFrom domain. -->
<xs:element name="envelope_from" type="xs:string"
minOccurs="0" maxOccurs="1"/>
<!-- The envelope recipient domain. -->
<xs:element name="envelope_to" type="xs:string"
minOccurs="0" maxOccurs="1"/>
</xs:all>
</xs:complexType>

<!-- DKIM verification result; see RFC 8601, Section 2.7.1. -->
<xs:simpleType name="DKIMResultType">
<xs:restriction base="xs:string">
<xs:enumeration value="none"/>
<xs:enumeration value="pass"/>
<xs:enumeration value="fail"/>
<xs:enumeration value="policy"/>
<xs:enumeration value="neutral"/>
<xs:enumeration value="temperror"/>
<xs:enumeration value="permerror"/>
</xs:restriction>
</xs:simpleType>

<xs:complexType name="DKIMAuthResultType">
<xs:all>
<!-- The "d=" tag in the signature. -->
<xs:element name="domain" type="xs:string"
minOccurs="1" maxOccurs="1"/>
<!-- The "s=" tag in the signature. -->
<xs:element name="selector" type="xs:string"
minOccurs="1" maxOccurs="1"/>
<!-- The DKIM verification result. -->
<xs:element name="result" type="DKIMResultType"
minOccurs="1" maxOccurs="1"/>
<!-- Any extra information (e.g., from Authentication-Results). -->
<xs:element name="human_result" type="langAttrString"
minOccurs="0" maxOccurs="1"/>
</xs:all>
</xs:complexType>

<!-- SPF domain scope. -->
<xs:simpleType name="SPFDomainScope">
<xs:restriction base="xs:string">
<xs:enumeration value="mfrom"/>
</xs:restriction>
</xs:simpleType>

<!-- SPF verification result; see RFC 8601, Section 2.7.2. -->
<xs:simpleType name="SPFResultType">
<xs:restriction base="xs:string">
<xs:enumeration value="none"/>
<xs:enumeration value="pass"/>
<xs:enumeration value="fail"/>
<xs:enumeration value="softfail"/>
<xs:enumeration value="policy"/>
<xs:enumeration value="neutral"/>
<xs:enumeration value="temperror"/>
<xs:enumeration value="permerror"/>
</xs:restriction>
</xs:simpleType>

<xs:complexType name="SPFAuthResultType">
<xs:all>
<!-- The checked domain. -->
<xs:element name="domain" type="xs:string"
minOccurs="1" maxOccurs="1"/>
<!-- The scope of the checked domain. -->
<xs:element name="scope" type="SPFDomainScope"
minOccurs="0" maxOccurs="1"/>
<!-- The SPF verification result. -->
<xs:element name="result" type="SPFResultType"
minOccurs="1" maxOccurs="1"/>
<!-- Any extra information (e.g., from Authentication-Results).
The information in the field below should be for a
person to be provided with additional information
that may be useful when debugging SPF authentication
issues. This could include broken records, invalid
DNS responses, etc. -->
<xs:element name="human_result" type="langAttrString"
minOccurs="0" maxOccurs="1"/>
</xs:all>
</xs:complexType>

<!-- This element contains DKIM and SPF results, uninterpreted
with respect to DMARC. -->
<xs:complexType name="AuthResultType">
<xs:sequence>
<!-- There may be zero or more DKIM signatures. -->
<xs:element name="dkim" type="DKIMAuthResultType"
minOccurs="0" maxOccurs="unbounded"/>
<!-- There may be zero or one SPF result. -->
<xs:element name="spf" type="SPFAuthResultType"
minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>

<!-- This element contains all the authentication results that
were evaluated by the receiving system for the given set of
messages. -->
<xs:complexType name="RecordType">
<xs:sequence>
<xs:element name="row" type="RowType"
minOccurs="1" maxOccurs="1"/>
<xs:element name="identifiers" type="IdentifierType"
minOccurs="1" maxOccurs="1"/>
<xs:element name="auth_results" type="AuthResultType"
minOccurs="1" maxOccurs="1"/>
<!-- Extension at record level -->
<xs:any processContents="lax"
minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>

<xs:complexType name="ExtensionType">
<xs:sequence>
<xs:any processContents="lax"
minOccurs="0" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>

<!-- Parent -->
<xs:element name="feedback">
<xs:complexType>
<xs:sequence>
<xs:element name="version" type="xs:decimal"
minOccurs="0" maxOccurs="1"/>
<xs:element name="report_metadata" type="ReportMetadataType"
minOccurs="1" maxOccurs="1"/>
<xs:element name="policy_published" type="PolicyPublishedType"
minOccurs="1" maxOccurs="1"/>
<!-- Extension at top level -->
<xs:element name="extension" type="ExtensionType"
minOccurs="0" maxOccurs="1"/>
<!-- One record (IP, result, IDs Auths) per tuple -->
<xs:element name="record" type="RecordType"
minOccurs="1" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>