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 相同,或 * 表示允許所有寄件者電子郵件。

範例

使用者在其電子郵件用戶端中開啟來自 sender@company.example 的電子郵件,方法是前往 https://emailclient.example/myinbox。該電子郵件使用 AMP 電子郵件,並使用 amp-listhttps://company.example/data.json 載入資料。

電子郵件用戶端將 HTTP 要求傳送至 https://company.example/data.json,並設定以下標頭

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

範例

使用者在其電子郵件用戶端中開啟來自 sender@company.example 的電子郵件,方法是前往 https://emailclient.example/myinbox。該電子郵件使用 AMP 電子郵件,並使用 amp-listhttps://company.example/data.json 載入資料。

電子郵件用戶端將 HTTP 要求傳送至 https://company.example/data.json?__amp_source_origin=sender@company.example (請注意新增的查詢字串),並設定以下標頭

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. senderEmailAMP-Email-Sender 標頭的值。
    2. 檢查 senderEmail 是否為您擁有或信任的電子郵件地址。如果不是,則拒絕要求。
    3. 將回應標頭 AMP-Email-Allow-Sender 設定為 senderEmail
  2. 如果已設定 Origin 標頭,但未設定 AMP-Email-Sender
    1. requestOriginOrigin 標頭的值。
    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 套件