CloudFront & quot; MalformedPolicy & quot; ошибка с подписанными URL

Мне нужно создать подписанные URL-адреса CloudFront с настраиваемой политикой с использованием PHP, но, независимо от того, что я делаю, моя политика явно «искажена».
Вот пример политики, сгенерированной в функции:

{"Statement":{"Resource":"https://d15xojelh58w5d.cloudfront.net/memo/kwz/cvyhkfdqn5oz0z1dz5at4z4s1jsn.kwz","Condition":{"DateLessThan":{"AWS:EpochTime":1490463203},"IpAddress":{"AWS:SourceIp":"1.2.3.4/32"}}}}

Сгенерированный URL:

https://d15xojelh58w5d.cloudfront.net/memo/kwz/cvyhkfdqn5oz0z1dz5at4z4s1jsn.kwz?Policy=eyJTdGF0ZW1lbnQiOnsiUmVzb3VyY2UiOiJodHRwczovL2QxNXhvamVsaDU4dzVkLmNsb3VkZnJvbnQubmV0L21lbW8va3d6L2N2eWhrZmRxbjVvejB6MWR6NWF0NHo0czFqc24ua3d6IiwiQ29uZGl0aW9uIjp7IkRhdGVMZXNzVGhhbiI6eyJBV1M6RXBvY2hUaW1lIjoxNDkwNDYzMjAzfSwiSXBBZGRyZXNzIjp7IkFXUzpTb3VyY2VJcCI6IjEuMi4zLjQvMzIifX19fQ__&Signature=MmBPtpipFLuNwaPliGLJajG4gJ7INwD0ptFdxPFYQP9CT-luq6W0SrAs9O9CqbJPHoukXwDzG~c88Rr5I2I9KP5QwD8MHpogGh~3SM3gBYm8ao0Zm7a5C9tWnBVRCtzuGrCrFstK-qLswWmqo6tNiOynSuFpvm9uDe3C8oWE2RzSZavEXoL35D3F8y98NeM0aOJe37EeSpdz3lrZZxei2TugoO-OmnApXa2YYJR2HiQ2l0t8paxcb3xyhCK1c1AR51uOpWLm63k~d0eNZJGo3x0Y6bx0GBqafdvV6jiUv6PbhiMC1ZcTxGnZhLmsz3~ONsEvaR1jyyOPt6y9Nos8yA__&Key-Pair-Id=APKAJ6RV6ACUX5M5IAOQ

Код:

function cloudfront_sign($url, $expiry = null, $ipLock = true) {
$policy = array(
'Statement' => array(
'Resource' => $url,
'Condition' => array(),
),
);

if(!$expiry || $expiry <= time()) $expiry = 2147483647; // CloudFront *requires* an expiry date, so set to 03:14:07 UTC on Tuesday, 19 January 2038 if one is not provided
$policy['Statement']['Condition']['DateLessThan'] = array('AWS:EpochTime' => $expiry);
if($ipLock) $policy['Statement']['Condition']['IpAddress'] = array('AWS:SourceIp' => $_SERVER['REMOTE_ADDR'].'/32');

$signer = new Aws\CloudFront\UrlSigner($_config['keyID'], $_config['keyPath']);

$jsonPolicy = json_encode($policy, JSON_UNESCAPED_SLASHES);

$url = $signer->getSignedUrl($url, null, $jsonPolicy);

return $url;
}
$url = kaeru_cloudfront_sign('https://d15xojelh58w5d.cloudfront.net/memo/kwz/cvyhkfdqn5oz0z1dz5at4z4s1jsn.kwz', 1490463203);

0

Решение

Я могу точно сказать вам, что происходит, но я не могу сказать вам, почему, если только это не ошибка в версии SDK, которую вы (очевидно) используете.

Политический документ действительно искажен, и подпись тоже.

И не похоже, что вы на самом деле делают что-то не так.

Я предполагаю, что вы знакомы с base64, где 8-битные данные расширяются до 6 бит на октет, чтобы позволить двоичным данным переноситься по транспортам, которые не обязательно являются 8-битными чистыми с использованием 64 символов (где 64 — 2 ^ 6, количество дискретных значений в 6 битах).

0-9 а также A-Z а также a-z составьте 10 + 26 + 26 = 62 необходимых символов, тогда есть + а также / довести общее количество символов до 64, но поскольку в конце может быть октет вывода с только 2 или 4 битами ввода, закодированный в него, 65-й символ = «заполнение» означает, что в предыдущем символе есть неиспользуемые биты, которые не представляют входные данные. Таким образом, любое представление base64 всегда заканчивается 0, 1 или 2 = символы. По этой причине закодированное в base64 значение никогда не может иметь = где угодно, кроме как в конце. Это оказывается важным, ниже.

Выбор символов + / = ужасно для URL из-за неоднозначности, вызванной тем, что очень многие пользовательские агенты (браузеры и клиентские библиотеки HTTP) обрабатывают их неправильно, когда дело доходит до экранирования url (также называемого url-кодировкой или процентным кодированием).

+ иногда считается эквивалентным %20 (пространство), в других случаях это сбежало как %2B= используется для разделения полей в строке запроса, поэтому некоторые пользовательские агенты экранируют его как %3D… а иногда / может быть экранирован как %2F… все это приводит к кошмару совместимости.

(Даже у самого S3 есть, по крайней мере, ошибка, связанная с неправильным экранированием URL-адреса, которая слишком долго была исправлена, чтобы исправить ее сейчас, потому что она сломала бы весь код, который был написан, чтобы предвидеть неправильное поведение S3. Но это не имеет отношения к проблеме под рукой.)

Дизайнеры CloudFront умело работали над этим.

CloudFront транслитерирует три потенциально проблемных символа следующим образом:

+ => -
/ => _
= => ~

Это работает, потому что персонажи - _ ~ не так часто подвержены ошибкам в URL.

Но как-то в вашем коде этот перевод неверен.

&Signature=...~ONsEvaR1jyyOPt6y9Nos8yA__&Key-...

Это определенно неправильно. ~ в середине подписи не может быть действительным. Как указано выше, ~ является = и это может быть действительным только на конец кодировки base64. Это означает, что __ в конце подпись и политика тоже неверны и должны быть на самом деле ~~,

?Policy=ey...X19fQ__&Signature=

Вы можете частично подтвердить это, поменяв их местами. Изменить __ в конце политики ~~ и вы обнаружите, что вы больше не видите Malformed Policy ошибка, потому что это, кажется, единственная проблема с политикой.

К сожалению, поскольку политика не содержит каких-либо ~ или же -, невозможно прийти к выводу о том, какие замены символов необходимы для того, чтобы сделать подпись действительной — не то, чтобы это было правильное решение, но оно действительно должно работать. Проблема в том, что мы не знаем, ~ а также _ транспонированы друг с другом, или если все три (включая -) неверны.

Но это определенно кажется проблемой с реальным кодом, который генерирует окончательный URL, не проблема с документом политики JSON, который вы ему предоставляете.

1

Другие решения

Других решений пока нет …