我需要使用PHP创建带有自定义策略的签名CloudFront URL,但无论我做什么,显然我的策略都是“格式错误”。 以下是函数中生成的示例策略:
{"Statement":{"Resource":"https://d15xojelh58w5d.cloudfront.net/memo/kwz/cvyhkfdqn5oz0z1dz5at4z4s1jsn.kwz","Condition":{"DateLessThan":{"AWS:EpochTime":1490463203},"IpAddress":{"AWS:SourceIp":"1.2.3.4/32"}}}}
生成的网址:
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 :(得分:1)
我可以准确地告诉你发生了什么,但是我无法告诉你为什么,除非它是你(显然)使用的SDK版本中的一个错误。
政策文件确实格式不正确,签名也是如此。
似乎你实际上做错了什么。
我假设您熟悉base64,其中8位数据扩展到每个八位位组6位,以允许通过使用64个符号(其中64位为2 ^)通过传输来传输二进制数据,这些传输不一定是8位清理6,离散值的数量为6位)。
0-9
和A-Z
以及a-z
组成10 + 26 + 26 = 62个必要符号,然后+
和/
带来总符号为64,但由于最后输出的八位位组只有2或4位输入编码,因此“填充”的第65个符号=
表示先前有未使用的位不代表输入数据的符号。因此,任何base64表示总是以0,1或2 =
个符号结尾。因此,除了最后,base64编码的值永远不会有=
。这在下面是重要的。
选择符号+
/
=
对于网址来说非常糟糕,因为很多用户代理(浏览器和HTTP客户端库)在处理错误时会导致模糊不清来到url-escaping(也称为url-encoding或percent-encoding)。
+
有时被视为等同于%20
(空格),有时则被转义为%2B
... =
用于分隔字段中的字段查询字符串,因此一些用户代理将其转义为%3D
...有时/
可能会转义为%2F
...所有这些都会导致互操作性的噩梦。
(即使S3本身至少还有与错误的url-escaping相关的bug,现在已经存在太长时间才能修复它,因为它会破坏为了预测S3的错误行为而编写的所有代码但这与手头的问题无关。)
CloudFront的设计师巧妙地解决了这个问题。
CloudFront音译三个可能存在问题的字符如下:
+ => -
/ => _
= => ~
这样做是因为字符-
_
~
在网址中几乎不容易出错。
但不知何故,在你的代码中,这种翻译是错误的。
&Signature=...~ONsEvaR1jyyOPt6y9Nos8yA__&Key-...
这绝对是错误的。签名中间的~
可能无效。如上所述,~
为=
,且只能在base64编码的 end 处有效。这意味着签名和策略末尾的__
也是错误的,实际上应该是~~
。
?Policy=ey...X19fQ__&Signature=
您可以通过交换它们来部分确认这一点。将策略末尾的__
更改为~~
,您会发现自己不再看到Malformed Policy
错误,因为这似乎是该政策的唯一问题。
不幸的是,由于该政策不包含任何~
或-
,因此无法得出关于使签名有效所需的字符替换的结论 - 不是这是正确的解决方案,但实际上应该可行。问题是,我们不知道是否只有~
和_
相互转置,或者是否所有三个(包括-
)都不正确。
但这肯定是生成最终URL的实际代码的问题,不您提供给它的JSON策略文档存在问题。