Подписанный URL-адрес AWS CloudFront приводит к отказу в доступе в случае использования альтернативного доменного имени и HTTPS

#node.js #https #amazon-cloudfront #signed-url

Вопрос:

Я настроил 4 дистрибутива CloudFront, все из которых имеют одинаковое происхождение S3 и имеют политику протокола просмотра: HTTP и HTTPS. Их отличие заключается в Альтернативных доменных именах и ограничении доступа зрителей.

  1. Распространение 1: Альтернативные Доменные Имена: Нет, Ограничение Доступа для просмотра: Нет
  2. Распределение 2: Альтернативные Доменные Имена: Нет, Ограничение Доступа к Просмотрщику: Да
  3. Распределение 3: Альтернативные Доменные Имена: distribution3.example.com, Ограничить Доступ к Просмотрщику: Нет
  4. Распределение 4: Альтернативные Доменные Имена: distribution4.example.com, Ограничить Доступ к Просмотрщику: Да

Я загрузил изображение в корзину S3 (доступ к нему заблокирован) и попытался получить к нему доступ с помощью дистрибутивов CloudFront. При подписании URL-адреса я использовал одно и то же значение для истечения срока действия (через неделю), использовал один и тот же открытый ключ в одной и той же группе доверенных ключей и подписал все URL-адреса одним и тем же кодом (с помощью политики сохранения). Мой домен зарегистрирован в Cloudflare, и его сертификат HTTPS выдается Amazon в диспетчере сертификатов AWS. Вот результаты:

Распространение 1: Альтернативные Доменные Имена: Нет, Ограничение Доступа для просмотра: Нет

 http://distribution1.cloudfront.net/image.jpg -> 200 OK
https://distribution1.cloudfront.net/image.jpg -> 200 OK
 

Распределение 2: Альтернативные Доменные Имена: Нет, Ограничение Доступа к Просмотрщику: Да

 http://distribution2.cloudfront.net/image.jpg -> 403 Forbidden
https://distribution2.cloudfront.net/image.jpg -> 403 Forbidden
http://distribution2.cloudfront.net/image.jpg?Expires=XXXamp;Signature=XXXamp;Key-Pair-Id=XXX -> 200 OK
https://distribution2.cloudfront.net/image.jpg?Expires=XXXamp;Signature=XXXamp;Key-Pair-Id=XXX -> 200 OK
 

Распределение 3: Альтернативные Доменные Имена: distribution3.example.com, Ограничить Доступ к Просмотрщику: Нет

 http://distribution3.cloudfront.net/image.jpg -> 200 OK
https://distribution3.cloudfront.net/image.jpg -> 200 OK
http://distribution3.example.com/image.jpg -> 200 OK
https://distribution3.example.com/image.jpg -> 200 OK
 

Распределение 4: Альтернативные Доменные Имена: distribution4.example.com, Ограничить Доступ к Просмотрщику: Да

 http://distribution4.cloudfront.net/image.jpg -> 403 Forbidden
https://distribution4.cloudfront.net/image.jpg -> 403 Forbidden
http://distribution4.example.com/image.jpg -> 403 Forbidden
https://distribution4.example.com/image.jpg -> 403 Forbidden
http://distribution4.cloudfront.net/image.jpg?Expires=XXXamp;Signature=XXXamp;Key-Pair-Id=XXX -> 200 OK
https://distribution4.cloudfront.net/image.jpg?Expires=XXXamp;Signature=XXXamp;Key-Pair-Id=XXX -> 200 OK
http://distribution4.example.com/image.jpg?Expires=XXXamp;Signature=XXXamp;Key-Pair-Id=XXX -> 200 OK
https://distribution4.example.com/image.jpg?Expires=XXXamp;Signature=XXXamp;Key-Pair-Id=XXX -> 403 -> Access denied XML page. WHY?
 

Почему последнее дело провалилось? Я что-то упускаю?

Код nodejs, используемый для создания подписанного URL-адреса, одинаков для всех:

 fs = require('fs')
crypto = require('crypto')

const EXPIRES = 1624235093   // Mon Jun 21 2021 00:24:53 GMT 0000
const KEY_PAIR_ID = 'KUXXX'  // Cloudfront -> Key management -> Public keys
const privateKey = fs.readFileSync('private_key.pem', 'utf8')

// Creating a signed URL using a canned policy
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/private-content-creating-signed-url-canned-policy.html
function createSignedURL(baseURL) {
  // canned policy
  const cannedPolicy = {
    "Statement": [
      {
        "Resource": baseURL,
        "Condition": {
          "DateLessThan": {
            "AWS:EpochTime": EXPIRES
          }
        }
      }
    ]
  }

  // Remove white space (including tabs and newline characters) from the policy string
  const cannedPolicyString = JSON.stringify(cannedPolicy).replace(/[stnr]/g, '')  
  // console.log(cannedPolicyString);

  // Sign policy with private key using RSA-SHA1, 
  // Base64-encode the string, 
  // Remove white space (including tabs and newline characters) from the hashed and signed string
  // Replace characters that are invalid in a URL query string with characters that are valid.
  const sign = crypto.createSign('RSA-SHA1')
  sign.update(cannedPolicyString)
  sign.end()
  const signature = sign.sign(privateKey, 'base64').replace(/ /g, '-').replace(/=/g, '_').replace(///g, '~')
  // console.log(signature);

  // Creating a signed URL
  const signedURL = `${ baseURL }?Expires=${ EXPIRES }amp;Signature=${ signature }amp;Key-Pair-Id=${ KEY_PAIR_ID }`
  // console.log(signedURL);

  // return
  return signedURL
}

console.log(createSignedURL('https://distribution4.example.com/image.jpg'));
 

Комментарии:

1. Не могли бы вы опубликовать код, который генерирует URL-адрес с подписью председателя, в дистрибутиве 4?

2. @jellycsc Я добавил код. Это было одинаково для всех сгенерированных подписей. Все они работали, кроме последнего.

3. Похоже, что вы подписываете http://, но пытаетесь получить доступ через http S ://

4. @anasqadrei Вы можете подтвердить, что подписываете https-версию URL-адреса^^?

5. @StefanN моя функция createSignedURL подписывает любой URL, который она получает, будь то http или https. Я подписываю протокол https и получаю доступ к протоколу https