AMP

AMP 电子邮件中的 CORS

跨域资源共享 (CORS) 是一种机制,它使用 HTTP 标头来告知浏览器哪些来源可以使用 XHR 访问资源。AMP 电子邮件通过添加 HTTP 标头来扩展此机制,这些标头类似地告知电子邮件客户端哪些发送者被允许访问这些资源。

目前此机制有两个版本。目前,建议您在服务器端支持这两个版本。

版本 2

当电子邮件通过 amp-formamp-list 或任何其他基于 XHR 的机制发出请求时,电子邮件客户端会包含以下 HTTP 标头

  • AMP-Email-Sender,设置为电子邮件发送者的电子邮件地址。

它期望 HTTP 响应返回包含以下标头

  • AMP-Email-Allow-Sender,其值与请求中的 AMP-Email-Sender 相同,或者为 *,表示允许所有发送者电子邮件。

示例

用户通过访问 https://emailclient.example/myinbox 在其电子邮件客户端中打开来自 sender@company.example 的电子邮件。该电子邮件使用 AMP 电子邮件,并使用来自 https://company.example/data.jsonamp-list 加载数据。

电子邮件客户端向 https://company.example/data.json 发送 HTTP 请求,并设置以下标头

AMP-Email-Sender: sender@company.example

电子邮件客户端期望 HTTP 响应包含以下标头

AMP-Email-Allow-Sender: sender@company.example

或者,允许在任一标头中使用 *(但不建议)。

版本 1(已弃用)

在版本 1 中,电子邮件客户端使用查询字符串参数而不是 HTTP 标头来指示发送者电子邮件。它还提供一个 Origin 标头,并且需要在响应中提供 Access-Control-Allow-Origin 标头,就像网站上的 CORS 一样。

当电子邮件通过 amp-formamp-list 或任何其他基于 XHR 的机制发出请求时,电子邮件客户端会包含以下 HTTP 标头

  • Origin,其值为用于显示电子邮件的页面的来源。

URL 也始终有一个查询字符串,其中 __amp_source_origin 参数设置为电子邮件发送者的电子邮件地址。

它期望 HTTP 响应包含以下标头

  • Access-Control-Allow-Origin,其值与请求中的 Origin 相同
  • AMP-Access-Control-Allow-Source-Origin,其值与请求中的 __amp_source_origin 查询字符串参数相同。
  • Access-Control-Expose-Headers 设置为 AMP-Access-Control-Allow-Source-Origin

示例

用户通过访问 https://emailclient.example/myinbox 在其电子邮件客户端中打开来自 sender@company.example 的电子邮件。该电子邮件使用 AMP 电子邮件,并使用来自 https://company.example/data.jsonamp-list 加载数据。

电子邮件客户端向 https://company.example/data.json?__amp_source_origin=sender@company.example 发送 HTTP 请求(请注意添加的查询字符串),并设置以下标头

Origin: https://emailclient.example

电子邮件客户端期望 HTTP 响应包含以下标头

Access-Control-Allow-Origin: https://emailclient.example
AMP-Access-Control-Allow-Source-Origin: sender@company.example
Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin

请注意,此版本不支持在 AMP-Access-Control-Allow-Source-Origin 标头中使用 *

实现 CORS

以下是在服务器端采取的实现 CORS 以支持版本 1 和 2 的推荐步骤

当您收到 HTTP 请求时,请检查是否设置了 OriginAMP-Email-Sender HTTP 标头。

  1. 如果设置了 AMP-Email-Sender 标头
    1. 让 *senderEmail* 为 AMP-Email-Sender 标头的值。
    2. 检查 *senderEmail* 是否是您拥有的或您信任的电子邮件地址。如果不是,则拒绝该请求。
    3. 将响应标头 AMP-Email-Allow-Sender 设置为 *senderEmail*。
  2. 如果设置了 Origin 标头,但未设置 AMP-Email-Sender
    1. 让 *requestOrigin* 为 Origin 标头的值。
    2. 将响应标头 Access-Control-Allow-Origin 设置为 *requestOrigin*。
    3. 检查 URL 是否包含 __amp_source_origin 查询字符串参数。如果没有,则拒绝该请求。
    4. 让 *senderEmail* 为 __amp_source_origin 查询字符串参数的值。
    5. 检查 *senderEmail* 是否是您拥有的或您信任的电子邮件地址。如果不是,则拒绝该请求。
    6. 将响应标头 AMP-Access-Control-Allow-Source-Origin 设置为 *senderEmail*。
    7. 将响应标头 Access-Control-Expose-Headers 设置为 AMP-Access-Control-Allow-Source-Origin
  3. 如果未设置 OriginAMP-Email-Sender,则拒绝该请求。

示例 1

电子邮件客户端发送的请求

GET /data.json?__amp_source_origin=sender@company.example HTTP/1.1
Host: company.example
Origin: https://emailclient.example
User-Agent: EmailClientProxy
Accept: application/json

预期的响应标头

Access-Control-Allow-Origin: https://emailclient.example
Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin
AMP-Access-Control-Allow-Source-Origin: sender@company.example

解释

由于设置了 Origin 标头,因此此请求正在使用 CORS 版本 1,并且需要设置上面列出的三个标头。

示例 2

电子邮件客户端发送的请求

GET /data.json HTTP/1.1
Host: company.example
AMP-Email-Sender: sender@company.example
User-Agent: EmailClientProxy
Accept: application/json

预期的响应标头

AMP-Email-Allow-Sender: sender@company.example

解释

由于设置了 AMP-Email-Sender 标头,因此此请求正在使用 CORS 版本 2,并且只需要 AMP-Email-Allow-Sender 标头。

示例 3

电子邮件客户端发送的请求

GET /data.json?__amp_source_origin=sender@company.example HTTP/1.1
Host: company.example
Origin: https://emailclient.example
AMP-Email-Sender: sender@company.example
User-Agent: EmailClientProxy
Accept: application/json

预期的响应标头

AMP-Email-Allow-Sender: sender@company.example

解释

同时设置了 OriginAMP-Email-Sender,表示客户端支持两个版本。由于版本 2 优先,因此仅设置 AMP-Email-Allow-Sender 标头,并且可以安全地忽略 Origin__amp_source_origin 的值。

代码示例

PHP

if (isset($_SERVER['HTTP_AMP_EMAIL_SENDER'])) {
    $senderEmail = $_SERVER['HTTP_AMP_EMAIL_SENDER'];
    if (!isAllowedSender($senderEmail)) {
        die('invalid sender');
    }
    header("AMP-Email-Allow-Sender: $senderEmail");
} elseif (isset($_SERVER['HTTP_ORIGIN'])) {
    $requestOrigin = $_SERVER['HTTP_ORIGIN'];
    if (empty($_GET['__amp_source_origin'])) {
        die('invalid request');
    }
    $senderEmail = $_GET['__amp_source_origin'];
    if (!isAllowedSender($senderEmail)) {
        die('invalid sender');
    }
    header("Access-Control-Allow-Origin: $requestOrigin");
    header('Access-Control-Expose-Headers: AMP-Access-Control-Allow-Source-Origin');
    header("AMP-Access-Control-Allow-Source-Origin: $senderEmail");
} else {
    die('invalid request');
}

Python (Django)

response = JsonResponse(...)
if request.META.HTTP_AMP_EMAIL_SENDER:
    senderEmail = request.META.HTTP_AMP_EMAIL_SENDER
    if not isAllowedSender(senderEmail):
        raise PermissionDenied
    response['AMP-Email-Allow-Sender'] = senderEmail
elif request.META.HTTP_ORIGIN:
    requestOrigin = request.META.HTTP_ORIGIN
    senderEmail = request.GET.get('__amp_source_origin')
    if not isAllowedSender(senderEmail):
        raise PermissionDenied
    response['Access-Control-Allow-Origin'] = requestOrigin
    response['Access-Control-Expose-Headers'] = 'AMP-Access-Control-Allow-Source-Origin'
    response['AMP-Access-Control-Allow-Source-Origin'] = senderEmail
else
    raise PermissionDenied

SSJS

<script runat="server" language="JavaScript">

Platform.Load("core", "1");

if (Platform.Request.GetRequestHeader("AMP-Email-Sender")) {
  var senderEmail = Platform.Request.GetRequestHeader("AMP-Email-Sender")
  if (isValidSender(senderEmail)) {
    HTTPHeader.SetValue("AMP-Email-Allow-Sender", senderEmail)
  } else {
    Platform.Function.RaiseError("Sender Not Allowed",true,"statusCode","3");
  }
} else if (Platform.Request.GetRequestHeader("Origin")) {
  var requestOrigin = Platform.Request.GetRequestHeader("Origin")

  if (Platform.Request.GetQueryStringParameter("__amp_source_origin")) {
    var senderEmail = Platform.Request.GetQueryStringParameter("__amp_source_origin");

    if (isValidSender(senderEmail)) {
      HTTPHeader.SetValue("Access-Control-Allow-Origin", requestOrigin);
      HTTPHeader.SetValue("Access-Control-Expose-Headers", "AMP-Access-Control-Allow-Source-Origin");
      HTTPHeader.SetValue("AMP-Access-Control-Allow-Source-Origin", senderEmail);
    } else {
      Platform.Function.RaiseError("Invalid Source Origin",true,"statusCode","3");
    }

  } else {
    Platform.Function.RaiseError("Source Origin Not Present",true,"statusCode","3");
  }
} else {
  Platform.Function.RaiseError("Origin and Sender Not Present",true,"statusCode","3");
}
</script>

访问 Salesforce 开发人员文档以了解有关 SSJS 的更多信息。

Node.js

使用官方支持的 @ampproject/toolbox-cors npm 包