Subscribe to receive notifications of new posts:

如何使用 Cloudflare Workers 构建 DMARC Management

03/17/2023

13 min read
How we built DMARC Management

DMARC 报告是什么

DMARC 表示“基于域的消息认证、报告和一致性”。这是一个电子邮件认证协议,有助于防止电子邮件钓鱼欺骗

当一封电子邮件发出时,DMARC 允许域名所有者建立一个 DNS 记录,指定使用哪些认证方法来验证电子邮件的真实性,例如 SPF (发件人策略框架) 和 DKIM (域名密钥识别邮件 )。当电子邮件不能通过这些身份验证时,DMARC 指示收件人的电子邮件服务提供商如何处理该邮件,予以隔离或直接拒绝。

当今的互联网上,随着电子邮件钓鱼和欺骗攻击变得越来越复杂和普遍,DMARC 中日益重要。通过实施 DMARC,域名所有者可以保护他们的品牌和客户免受这些攻击的负面影响,包括失去信任、声誉受损和经济损失。

除了防止网络钓鱼和欺骗攻击,DMARC 还提供了报告功能。域名所有者可以收到关于电子邮件身份验证活动的报告,包括哪些消息通过了,哪些消息 DMARC 检查失败,以及这些消息的来源。

DMARC 管理涉及域的 DMARC 策略配置和维护。有效的 DMARC 管理需要对电子邮件认证活动进行持续监控和分析,并能够根据需要对 DMARC 策略进行调整和更新。

有效的 DMARC 管理包括的一些关键组成部分如下:

  • 设置 DMARC 策略:这涉及配置域的 DMARC 记录,以指定适当的身份验证方法,以及处理身份验证检查失败消息的策略。下面是一个 DMARC DNS 记录的示例:

v=DMARC1; p=reject; rua=mailto:[email protected]

这指定了我们将使用 DMARC 版本 1,我们的策略是拒绝 DMARC 检查失败的电子邮件,以及提供商应该将 DMARC 报告发送到哪个电子邮件地址。

  • 监控电子邮件验证活动:DMARC 报告是域名所有者确保电子邮件安全性和可交付性,以及遵守行业标准和法规的重要工具。通过定期监控和分析 DMARC 报告,域名所有者可以识别电子邮件威胁,优化电子邮件活动,并改善整体的电子邮件验证。
  • 根据需要进行调整:根据对 DMARC 报告的分析,域名所有者可能需要对 DMARC 策略或验证方式进行调整,以确保邮件正确验证并防止钓鱼和欺骗攻击。
  • 与电子邮件提供商和第三方供应商合作:有效的 DMARC 管理可能需要与电子邮件提供商和第三方供应商合作,以确保正确实施和执行 DMARC 策略。

今天我们推出了 DMARC 管理。如下介绍我们如何打造这个功能。

我们的网络是如何打造的

作为云安全与性能解决方案的领先提供商,Cloudflare 采用一种特定的方法来测试我们的产品。我们会亲身使用我们自己的工具和服务,也就是用来运营我们的业务。这有助于我们在任何问题或 bug 影响到客户之前识别它们。

我们在内部使用自己的产品,例如 Cloudflare Workers,这是一个无服务器平台,允许开发人员在我们的全球网络上运行自己的代码。自 2017 年推出以来,Workers 生态系统已经显著增长。今天,数以万计的开发人员在这个平台上构建和部署应用程序。Workers 生态系统的力量在于它能够让开发人员构建复杂的应用程序,这些应用程序在以前不可能或无法以可行的方式在如此接近客户的地方运行。Worker 可以用于构建 API、生成动态内容、优化图像、执行实时处理等。可能性几乎是无穷无尽的。我们使用 Workers 来驱动像 Radar 2.0 这样的服务,或者像 Wildebeest 这样的软件包。

最近,我们的 Email Routing(电子邮件路由)与 Workers 携手合作,实现通过 Workers 脚本处理传入电子邮件的功能。正如文档所述:“通过 Email Workers,您可以利用 Cloudflare Workers 的强大功能来实现处理电子邮件所需的任何逻辑和和创建复杂的规则。这些规则决定了您收到邮件后会发生什么。” 规则和经验证的地址都可以通过我们的 API 配置。

这里是一个简单的 Email Worker 示例:

export default {
  async email(message, env, ctx) {
    const allowList = ["[email protected]", "[email protected]"];
    if (allowList.indexOf(message.headers.get("from")) == -1) {
      message.setReject("Address not allowed");
    } else {
      await message.forward("inbox@corp");
    }
  }
}

相当简单明了,对吧?

凭借以编程方式处理传入电子邮件的能力,它似乎是以可扩展和高效的方式处理传入 DMARC 报告电子邮件的完美方式,让 Email Routing 和 Workers 负担从全球接收不限制数量电子邮件的繁重工作。有关我们需求的概述如下:

  1. 接收邮件并提取报告
  2. 发布相关细节到分析平台
  3. 存储原始报告

Email Workers 使我们能够轻松地做到第一点。我们只需要创建一个带有 email() 处理器的 Worker。该处理器将接收 SMTP 信封元素,预解析版本的电子邮件头,以及一个用于读取完整原始电子邮件的流。

对于第二点,我们也可以考虑 Workers 平台,我们将找到 Workers Analytics Engine。我们只需要定义一个适当的模式,这取决于报告中存在的内容和我们计划稍后执行的查询。之后,我们可以使用 GraphQLSQL API 来查询数据。

对于第三点,我们的 R2 对象存储就完全可以胜任。从 Worker 访问 R2 是小菜一碟 。从电子邮件中提取报告后,我们将它们存储在 R2 中以供后续使用。

我们将此构建为一个托管服务,可在您的区域上启用,并添加了一个仪表板界面以便使用,但实际上,所有工具都提供给您使用,在您自己的帐户中, 在 Cloudflare Workers 基础上部署自己的 DMARC 报告处理器,无需不必担心服务器、可扩展性或性能。

架构

Email Workers 是我们 Email Routing 产品的一个功能。Email Routing 组件在我们的所有节点中运行,因此它们中的任何一个都能够处理传入的邮件,这是很重要的,因为我们从所有数据中心宣布电子邮件的入口 BGP 前缀。向 Email Worker 发送电子邮件很容易,只需在 Email Routing 仪表板中设置一个规则。

当该 Email Routing 组件收到与要发送给 Worker 的规则匹配的电子邮件时,它将联系我们最近开源的 workerd 运行时的一个内部版本,后者也运行于所有节点上。管理这种交互的 RPC 模式在 Capnproto 模式中定义,允许在读取电子邮件正文时将其流式传输给 Edgeworker。如果 Worker 脚本决定转发这封邮件,Edgeworker 将使用原始请求中发送的一个功能联系 Email Routing。

jsg::Promise<void> ForwardableEmailMessage::forward(kj::String rcptTo, jsg::Optional<jsg::Ref<Headers>> maybeHeaders) {
  auto req = emailFwdr->forwardEmailRequest();
  req.setRcptTo(rcptTo);

  auto sendP = req.send().then(
      [](capnp::Response<rpc::EmailMetadata::EmailFwdr::ForwardEmailResults> res) mutable {
    auto result = res.getResponse().getResult();
    JSG_REQUIRE(result.isOk(), Error, result.getError());
  });
  auto& context = IoContext::current();
  return context.awaitIo(kj::mv(sendP));
}

就 DMARC 报告而言,我们是这样处理传入电子邮件的:

  1. 获取正在处理的电子邮件的收件人,这里使用了 RUA。RUA 是一个 DMARC 配置参数,它表示有关某个域的聚合 DMARC 处理应该反馈到哪里。这个收件人可以在邮件的 “to” 属性中找到。
const ruaID = message.to
  1. 由于我们为不限制数量的域处理 DMARC 报告,我们使用 Workers KV 来存储每个域的一些信息,并将这些信息存储在 RUA上。这也让我们知道我们是否应该收到这些报告。
const accountInfoRaw = await env.KV_DMARC_REPORTS.get(dmarc:${ruaID})
  1. 此时,我们需要将整封电子邮件读取到 arrayBuffer 中以便解析。根据报告的大小,我们可能会遇到免费 Workers 计划的限制。如果发生这种情况,我们建议您切换到不存在这个问题的 Workers Unbound 资源模型。
const rawEmail = new Response(message.raw)
const arrayBuffer = await rawEmail.arrayBuffer()
  1. 解析原始电子邮件包括解析它的 MIME 部分。有多个可用的库允许这样做。例如,您可以使用 postal-mime:
const parser = new PostalMime.default()
const email = await parser.parse(arrayBuffer)
  1. 解析完电子邮件后,我们现在可以访问它的附件。这些附件是 DMARC 报告本身,可以被压缩。我们要做的第一件事是将它们以压缩形式存储在 R2 以便长期存储。它们在以后重新处理或调查值得注意的报告时可能很有用。这个操作很简单,只需要在 R2 绑定上调用 put()。为了便于日后检索,我们建议您根据当前时间将报表文件分散到多个目录中。
await env.R2_DMARC_REPORTS.put(
    `${date.getUTCFullYear()}/${date.getUTCMonth() + 1}/${attachment.filename}`,
    attachment.content
  )
  1. 我们现在需要查看附件的 mime 类型。DMARC 报告的原始格式是 XML,但是可以被压缩。在这种情况下,我们需要先解压它们。DMARC 报告文件可以使用多种压缩算法。我们使用 MIME 类型来确定使用哪一个。对于 Zlib 压缩的报告,可使用 pako ,而对于 ZIP 压缩报告,unzipit 就是很好的选择。

  2. 在获得报告的原始 XML 格式后,fast-xml-parser 可以很好地为我们解析它们。下面是 DMARC 报告的XML 示例:

<feedback>
  <report_metadata>
    <org_name>example.com</org_name>
    <[email protected]</email>
   <extra_contact_info>http://example.com/dmarc/support</extra_contact_info>
    <report_id>9391651994964116463</report_id>
    <date_range>
      <begin>1335521200</begin>
      <end>1335652599</end>
    </date_range>
  </report_metadata>
  <policy_published>
    <domain>business.example</domain>
    <adkim>r</adkim>
    <aspf>r</aspf>
    <p>none</p>
    <sp>none</sp>
    <pct>100</pct>
  </policy_published>
  <record>
    <row>
      <source_ip>192.0.2.1</source_ip>
      <count>2</count>
      <policy_evaluated>
        <disposition>none</disposition>
        <dkim>fail</dkim>
        <spf>pass</spf>
      </policy_evaluated>
    </row>
    <identifiers>
      <header_from>business.example</header_from>
    </identifiers>
    <auth_results>
      <dkim>
        <domain>business.example</domain>
        <result>fail</result>
        <human_result></human_result>
      </dkim>
      <spf>
        <domain>business.example</domain>
        <result>pass</result>
      </spf>
    </auth_results>
  </record>
</feedback>
  1. 现在我们已经掌握了报告的所有数据。接下来要做什么很大程度上取决于我们想如何呈现数据。对我们来说,目标是从其中提取有意义的数据并显示在仪表板上。因此,我们需要一个分析平台来推送丰富的数据。Workers Analytics Engine登场了。该分析引擎非常适合完成这项任务,它允许我们从一个 Worker 向其发送数据,并公布一个 GraphQL API 供后面与数据交互。这就是我们获取数据并在仪表板中显示的方式。

将来,我们还考虑在工作流中集成 Queues 以异步处理报告,避免等待客户端完成。

我们仅依靠 Workers 基础设施就端到端实现了这个项目,这证明了,在不必担心可扩展性、性能、存储和安全问题的情况下,构建复杂的应用程序是可能的,也是有益的。

开源

如前所述,我们构建了一个托管服务,供您启动和适用。但是,我们所做的一切也可以由您在自己的帐户中部署,以便管理自己的 DMARC 报告。这很简单,而且免费。为了帮助您完成这项工作,我们为按照以上方式处理 DMARC 报告的 Worker 发布了一个开源版本:https://github.com/cloudflare/dmarc-email-worker

如果您没有仪表板来显示数据,您也可以从一个 Worker 查询 Analytics Engine。或者,如果您想将其存储到一个关系数据库中,那么 D1 就可以派上用场了。可能性是无限的,我们很想知道您将使用这些工具打造出什么。

欢迎贡献,自行创作,我们将拭目以待。

结语

我们希望本文能加深您对 Workers 平台的了解。今天 Cloudflare 利用这个平台来构建我们的大部分服务,我们认为您也应该这样做。

欢迎随时为我们的开源版本做出贡献,并向我们展示您可以用它做什么。

Email Routing 也在努力扩展 Email Workers API 的功能性,有关内容将专文介绍。

We protect entire corporate networks, help customers build Internet-scale applications efficiently, accelerate any website or Internet application, ward off DDoS attacks, keep hackers at bay, and can help you on your journey to Zero Trust.

Visit 1.1.1.1 from any device to get started with our free app that makes your Internet faster and safer.

To learn more about our mission to help build a better Internet, start here. If you're looking for a new career direction, check out our open positions.
Security Week (CN)Email Security (CN)Security (CN)DMARC (CN)简体中文

Follow on X

André Cruz|@edevil
Cloudflare|@cloudflare

Related posts

March 08, 2024 2:05 PM

Log Explorer:在没有第三方存储的情况下监视安全事件

借助 Security Analytics + Log Explorer 的综合功能,安全团队可以在 Cloudflare 中本地分析、调查和监控安全攻击,无需将日志转发给第三方 SIEM,从而缩短解决时间并降低客户的总体拥有成本...