CloudFront "MalformedPolicy" 签名 URL 错误
CloudFront "MalformedPolicy" error with signed URLs
我需要使用 PHP 创建带有自定义策略的已签名 CloudFront URL,但无论我做什么,显然我的策略都是 "malformed"。
这是函数中生成的示例策略:
{"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);
我可以确切地告诉你发生了什么,但我不能告诉你为什么,除非它是你正在(显然)使用的 SDK 版本中的错误。
保单文件确实有问题,签名也是。
而且你似乎并没有做错什么。
我假设您熟悉 base64,其中 8 位数据被扩展为每八位字节 6 位,以允许二进制数据通过使用 64 个符号(其中 64 是2^6,6位中离散值的个数)。
0-9
和A-Z
和a-z
组成了10+26+26=62个必要的符号,然后就是+
和/
使符号总数达到 64,但由于最后可能有一个八位字节的输出,其中只有 2 或 4 位输入被编码,因此 "padding" 的第 65 个符号 =
表示未使用不代表输入数据的先验符号中的位。因此,任何 base64 表示总是以 0、1 或 2 =
符号结尾。出于这个原因,base64 编码的值永远不会有 =
除了最后的任何地方。事实证明这很重要,如下所示。
符号 +
/
=
的选择对于 URL 来说是可怕的,因为太多的用户代理(浏览器和 HTTP 客户端库)导致歧义) 在 url-转义(也称为 url-编码或百分比编码)时处理不正确。
+
有时被认为等同于 %20
(space),其他时候它被转义为 %2B
... 使用 =
分隔查询字符串中的字段,因此一些用户代理将其转义为 %3D
... 有时 /
可能会转义为 %2F
... 所有这些都会导致互操作性噩梦。
(甚至 S3 本身也至少有与不正确的 url-转义相关的错误,该错误已经存在太久以至于无法修复,现在,因为它会破坏所有写入的代码预测 S3 的不正确行为。但这与手头的问题无关。)
CloudFront 的设计者巧妙地解决了这个问题。
CloudFront 将三个可能有问题的字符音译如下:
+ => -
/ => _
= => ~
这是可行的,因为字符 -
_
~
在 URL 中几乎没有那么容易出错。
但不知何故,在您的代码中,这种翻译是错误的。
&Signature=...~ONsEvaR1jyyOPt6y9Nos8yA__&Key-...
这绝对是错误的。签名中间的 ~
不可能有效。如上所述,~
是 =
并且只能在 base64 编码的 end 处有效。这意味着签名和策略末尾的 __
也是错误的,实际上应该是 ~~
.
?Policy=ey...X19fQ__&Signature=
你可以通过换掉它们来部分确认这一点。将策略末尾的 __
更改为 ~~
,您会发现您不再看到 Malformed Policy
错误,因为这似乎是该策略的唯一问题。
不幸的是,由于该策略不包含任何 ~
或 -
,因此无法得出需要哪些字符替换才能使签名有效的结论 --并不是说这是正确的解决方案,但它应该确实有效。问题是,我们不知道是否只有 ~
和 _
相互转置,或者三个(包括 -
)都不正确。
但这显然是生成最终 URL 的实际代码的问题,不是 JSON 策略文档的问题你正在供应它。
我需要使用 PHP 创建带有自定义策略的已签名 CloudFront URL,但无论我做什么,显然我的策略都是 "malformed"。 这是函数中生成的示例策略:
{"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);
我可以确切地告诉你发生了什么,但我不能告诉你为什么,除非它是你正在(显然)使用的 SDK 版本中的错误。
保单文件确实有问题,签名也是。
而且你似乎并没有做错什么。
我假设您熟悉 base64,其中 8 位数据被扩展为每八位字节 6 位,以允许二进制数据通过使用 64 个符号(其中 64 是2^6,6位中离散值的个数)。
0-9
和A-Z
和a-z
组成了10+26+26=62个必要的符号,然后就是+
和/
使符号总数达到 64,但由于最后可能有一个八位字节的输出,其中只有 2 或 4 位输入被编码,因此 "padding" 的第 65 个符号 =
表示未使用不代表输入数据的先验符号中的位。因此,任何 base64 表示总是以 0、1 或 2 =
符号结尾。出于这个原因,base64 编码的值永远不会有 =
除了最后的任何地方。事实证明这很重要,如下所示。
符号 +
/
=
的选择对于 URL 来说是可怕的,因为太多的用户代理(浏览器和 HTTP 客户端库)导致歧义) 在 url-转义(也称为 url-编码或百分比编码)时处理不正确。
+
有时被认为等同于 %20
(space),其他时候它被转义为 %2B
... 使用 =
分隔查询字符串中的字段,因此一些用户代理将其转义为 %3D
... 有时 /
可能会转义为 %2F
... 所有这些都会导致互操作性噩梦。
(甚至 S3 本身也至少有与不正确的 url-转义相关的错误,该错误已经存在太久以至于无法修复,现在,因为它会破坏所有写入的代码预测 S3 的不正确行为。但这与手头的问题无关。)
CloudFront 的设计者巧妙地解决了这个问题。
CloudFront 将三个可能有问题的字符音译如下:
+ => -
/ => _
= => ~
这是可行的,因为字符 -
_
~
在 URL 中几乎没有那么容易出错。
但不知何故,在您的代码中,这种翻译是错误的。
&Signature=...~ONsEvaR1jyyOPt6y9Nos8yA__&Key-...
这绝对是错误的。签名中间的 ~
不可能有效。如上所述,~
是 =
并且只能在 base64 编码的 end 处有效。这意味着签名和策略末尾的 __
也是错误的,实际上应该是 ~~
.
?Policy=ey...X19fQ__&Signature=
你可以通过换掉它们来部分确认这一点。将策略末尾的 __
更改为 ~~
,您会发现您不再看到 Malformed Policy
错误,因为这似乎是该策略的唯一问题。
不幸的是,由于该策略不包含任何 ~
或 -
,因此无法得出需要哪些字符替换才能使签名有效的结论 --并不是说这是正确的解决方案,但它应该确实有效。问题是,我们不知道是否只有 ~
和 _
相互转置,或者三个(包括 -
)都不正确。
但这显然是生成最终 URL 的实际代码的问题,不是 JSON 策略文档的问题你正在供应它。