Back

Web 端邮箱混淆技术

Web 端邮箱混淆技术

每一个公开的邮箱地址都是攻击目标。当你在 HTML 中放入一个裸露的 mailto: 链接时,自动化的爬虫程序可以迅速收集到它。本文将介绍前端开发者最实用的邮箱混淆技术——每种技术的原理、面对现代爬虫工具的有效程度,以及它们在可用性或可访问性方面的不足。

需要预先说明一个重要事项:邮箱混淆并不能保证安全。它只能减少自动化采集,无法阻止有针对性的收集。无头浏览器、AI 辅助爬虫和 OCR 工具的出现,使得几种较老的技术远不如以往可靠。我们的目标是提高采集成本,而非让发现变得不可能。

核心要点

  • 裸露的 mailto: 链接极易被爬取,会同时在 href 属性和可见的 DOM 中暴露你的地址。
  • HTML 实体编码对基于简单正则的爬虫意外地有效,但对无头浏览器毫无防御能力。
  • 基于 JavaScript 的技术(字符串拼接、转换函数、通过 SubtleCrypto 实现的 AES)显著提高了基础爬虫的门槛。
  • CSS 技巧如反转文本或 ::after 伪元素会损害可用性和可访问性——应避免使用。
  • 联系表单避免在页面 HTML 中暴露地址,但应配合蜜罐字段或注重隐私的 CAPTCHA(如 Cloudflare Turnstile)使用。
  • 多种技术分层组合可提供最佳的实际防护效果。

为什么裸露的 mailto 链接是个问题

一个未受保护的 mailto: 链接,例如:

<a href="mailto:contact@example.com">Email us</a>

极易被爬取。基于正则的机器人能瞬间发现它。它在两个地方暴露了你的地址:href 属性,以及——如果你将地址显示为链接文本——可见的 DOM。要想有效减少垃圾邮件,这两处都需要保护。

HTML 实体编码

将邮箱地址中的每个字符替换为其对应的 HTML 实体,是最古老的混淆技术之一:

<a href="&#109;&#97;&#105;&#108;&#116;&#111;&#58;&#99;&#111;&#110;&#116;&#97;&#99;&#116;&#64;&#101;&#120;&#97;&#109;&#112;&#108;&#101;&#46;&#99;&#111;&#109;">Email us</a>

浏览器会正确解码并渲染出可用的 mailto 链接。Spencer Mortensen 的蜜罐测试发现,这种方式拦截了高比例针对可点击链接的爬虫——考虑到服务端库可以轻松解码实体,这种效果令人意外。它之所以有效,是因为大多数爬虫并不复杂。然而,它对无头浏览器或任何处理渲染后 DOM(而非原始 HTML)的爬虫毫无防御作用。

可用性: 无影响。可访问性: 完全兼容。可维护性: 手写繁琐——请使用生成器。

基于 JavaScript 的混淆

JavaScript 技术将邮箱地址完全从静态 HTML 中移除,这显著提高了基础爬虫的门槛。

字符串拼接 将地址拆分到多个字符串片段中,在运行时进行组装。完整地址永远不会出现在 HTML 源码中,只在执行后存在于内存里。

<a id="contact-link" href="#">Email us</a>
<script>
  const user = "contact";
  const domain = "example.com";
  const link = document.getElementById("contact-link");
  link.href = "mailto:" + user + "@" + domain;
  link.textContent = user + "@" + domain;
</script>

自定义转换函数 更进一步。HTML 源码中包含无意义的占位文本,只有在页面在真实浏览器环境中渲染时,一个小型 JS 函数才会将其转换为有效地址。这是 mailto 链接防垃圾邮件最有效的方法之一,因为它需要完整的 JavaScript 执行才能还原地址。

AES 加密 使用浏览器原生的 SubtleCrypto API,在构建时加密地址,并在客户端解密。由于 SubtleCrypto 只能在安全上下文中运行,这要求使用 HTTPS——而你本就应该已经在使用。根据 Can I Use 的数据,现代浏览器已广泛支持该 API。

重要局限: 这些技术都无法阻止 Puppeteer 或 Playwright 等无头浏览器,它们能完整执行 JavaScript。但它们能阻止大多数仍然基于正则、只处理原始 HTML 的爬虫。

可用性: 实现得当时极佳。可访问性: 取决于实现——确保渲染出的链接可通过键盘导航且对屏幕阅读器友好。可维护性: 中等。

应避免的 CSS 技术

一些 CSS 方法——反转文本方向、屏幕外定位或 ::after 伪元素——看似巧妙,但严重破坏可用性。通过 ::after 渲染的文本无法被选中或复制。反转文本即便能复制也会让用户困惑。这些技术也无法抵御任何同时解析 CSS 和 HTML 的爬虫。请避免使用。

联系表单作为替代方案

用联系表单替代公开的邮箱地址,可以完全避免在页面 HTML 中暴露地址。代价是可用性:许多用户更倾向于直接发邮件,而较长的表单会降低转化率。

如果你使用联系表单,请保护好它。机器人可以自动提交表单。添加一个蜜罐字段——一个真实用户从不填写但机器人通常会填写的隐藏输入框——作为轻量级、可访问的第一层防护。对于流量较大的表单,Cloudflare Turnstile 提供了一种注重隐私的 CAPTCHA 替代方案,比 reCAPTCHA v2 的摩擦更小。

可访问性提示: 基于图像的 CAPTCHA 对视障用户造成实质性障碍。务必提供音频替代方案,或选择不依赖纯视觉挑战的 CAPTCHA 方案。WCAG 2.2 指南是一个很好的参考依据。

Cloudflare 邮箱地址混淆

如果你的网站在 Cloudflare 后端运行,其内置的邮箱地址混淆功能值得启用。Cloudflare 会在边缘节点重写你 HTML 中的邮箱地址,在页面到达客户端之前完成,然后注入一个延迟加载的小型解码脚本(email-decode.min.js),在浏览器中将其还原。该脚本以 defer 方式加载,因此不会阻塞渲染。

这种方法对用户实际上是透明的,且无需对代码库做任何修改。主要限制在于它不适用于 <script><noscript><textarea><head> 标签内部,并且如果你的页面带有 Cache-Control: no-transform 响应头,它也无法工作。

通过分层组合提升防护覆盖

没有任何单一技术能够独立胜任。对大多数网站而言,一个实用的组合是:

  • 使用 JavaScript 转换或 AES 加密 保护 mailto 链接的 href 属性。
  • 对任何可见的地址文本应用 HTML 实体编码 作为辅助层。
  • 添加带有 蜜罐字段的联系表单 作为替代联系方式。
  • 如果你已在 Cloudflare 网络上,启用 Cloudflare 邮箱混淆

这种分层方法同时覆盖了链接属性和可见文本,弥合了仅读取原始 HTML 的爬虫与执行 JavaScript 的爬虫之间的差距。

结论

邮箱混淆能切实减少自动化采集。蜜罐数据一致表明,即便是基础技术也能拦截大量爬虫,因为许多采集者仍然不够先进。但混淆并不能替代优秀的垃圾邮件过滤器,也无法阻止有决心的、针对性的收集。

实施一两种可靠的技术,在合理之处分层使用,然后继续前进。节省下来的时间最好用在别处。

常见问题

是的,但仅对不复杂的爬虫有效。蜜罐测试表明,它仍能阻挡许多基于正则的爬虫,因为它们读取原始 HTML 时不会解码实体。然而,任何解析渲染后 DOM 的爬虫(包括 Puppeteer 等无头浏览器)都能看到解码后的地址。请将其作为更广泛策略中的一层使用,而非独立防御。

可以。Puppeteer 和 Playwright 等无头浏览器能完整执行 JavaScript,因此任何依赖运行时解码的技术——包括字符串拼接、转换函数和 AES 解密——都能被它们击败。基于 JS 的混淆的价值在于阻止数量更庞大的基于正则的爬虫,后者仍占据自动化采集流量的很大比例。

这取决于你的目标。联系表单避免了在页面 HTML 中暴露邮箱地址,对基础采集提供了更强的保护。但表单会降低转化率,且许多用户更倾向于直接发邮件。一种平衡的做法是同时提供两者:为愿意使用的用户提供混淆的 mailto 链接,并配以带蜜罐字段的受保护联系表单作为后备。

通常不会有实质性影响,但仍应在真实页面和辅助技术上进行测试。解码脚本会在页面加载后于客户端还原地址,之后键盘导航通常可以正常工作。搜索引擎一般不将邮箱地址视为排名信号,因此实际的 SEO 影响很小。只需确保你的页面没有携带 Cache-Control no-transform 响应头即可。

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers. Check our GitHub repo and join the thousands of developers in our community.

OpenReplay