PHP。AmazonS3签名V4。简单例子

PHP。AmazonS3签名V4。简单例子,php,amazon-s3,Php,Amazon S3,我在我的PHP应用程序中使用了一组简单的函数上传到AmazonS3。 我的代码使用签名版本2。一切都很顺利。但它不适用于不支持签名V2的新地区 我尝试实现签名V4,但它看起来太复杂了。我只是无法为签名V4制作工作示例 支持V4登录AWS SDK for PHP。但这对我来说太复杂了。我不能使用那个SDK(因为PHP5.2,我不能升级它) 在我当前的代码中,我有V2的简单代码 $stringToSign="PUT\n\n$contentType\n$httpDate\nx-amz-acl:$acl

我在我的PHP应用程序中使用了一组简单的函数上传到AmazonS3。 我的代码使用签名版本2。一切都很顺利。但它不适用于不支持签名V2的新地区

我尝试实现签名V4,但它看起来太复杂了。我只是无法为签名V4制作工作示例

支持V4登录AWS SDK for PHP。但这对我来说太复杂了。我不能使用那个SDK(因为PHP5.2,我不能升级它)

在我当前的代码中,我有V2的简单代码

$stringToSign="PUT\n\n$contentType\n$httpDate\nx-amz-acl:$acl\n";
$stringToSign.="/$resource";
$signature = $this->constructSig($stringToSign);
$req->addHeader("Authorization", "AWS " . $this->accessKeyId . ":" . $signature);

function constructSig($str) {
    $hasher = new Crypt_HMAC($this->secretKey, "sha1");
    $signature = $this->hex2b64($hasher->hash($str));
    return($signature);
}
有没有可能使用这样简单的constructSigV4函数来创建新的签名类型

另外,我明白了,新类型签名需要有bucket区域。如果我不知道桶区域,那怎么办?每次在执行请求之前请求存储桶区域?我在不同的地区有水桶

更新。

这里有一个简单的例子

我似乎创造了一个有效的例子。签名似乎通过了。我犯了“签名不匹配”之类的错误。然后我改正了,直到这个错误消失。 但现在我有另一个错误:

亚马逊的回应是:

HTTP/1.1 411 Length Required

<?xml version="1.0" encoding="UTF-8"?>
<Error><Code>MissingContentLength</Code><Message>You must provide the Content-Length HTTP header.</Message>
但我的请求中包含了内容长度

PUT /259001/checkmark-circle.png HTTP/1.1
Host: ******.s3.amazonaws.com
Content-Type: image/png
Content-Length: 2338
x-amz-acl: private
x-amz-content-sha256: STREAMING-AWS4-HMAC-SHA256-PAYLOAD
X-AMZ-Date: Tue, 19 Jan 2016 07:33:01 -0500
Date: Tue, 19 Jan 2016 07:33:01 -0500
Authorization: AWS4-HMAC-SHA256 Credential=************/20160119/us-east-1/s3/aws4_request,SignedHeaders=host;x-amz-acl;x-amz-content-sha256;x-amz-date,Signature=c2bb290f2d54d31bc95785705530e10e0a8bde4c7b7dd1ef3b11a8fa7d96a57c
有什么不对劲?如果存在,为什么会说内容长度会丢失?

有关如何了解任何存储桶的区域,请参阅。tl;dr:您可以要求S3的任何区域为您提供一个存储桶的位置,或者您可以使用嵌入在错误中的信息,如果该区域是错误的

Sqlbot针对AWS的最佳实践(这是一本我还没有真正写过的书)会告诉您,跟踪每个bucket位置的理想解决方案是使用适当的区域主机名规范化存储的每个S3URI。对于
us-west-2
中名为
example
的bucket,有效URL包括:

https://example.s3.amazonaws.com/
https://example.s3-us-west-2.amazonaws.com/
这两种格式都会将请求发送到正确的区域,因为第一种格式
*.s3.amazonaws.com
使用s3本身管理的DNS自动将请求发送到正确的区域端点,而第二种格式在主机名中显式包含区域端点。由于不同区域之间存在一致的模式,因此如果以这种方式在内部存储URL,则可以相对轻松地从主机名中提取正确的区域

注意:
us-east-1
地区是上述情况的例外,出于遗留原因,该地区希望主机名中包含
s3.amazonaws.com
s3-external-1.amazonaws.com
。您的代码将需要处理此情况

除了维护bucket到region映射的配置表之外,以这种形式存储S3对象引用可能是处理签名版本4所需的特定于区域的身份验证的最佳方式

乍一看,签名V4看起来确实比V2复杂得多,事实上,签名代码并不像您为V2显示的那样紧凑,但是如果您设法使V2正常工作,我相信您可以使V4正常工作

有一个官方签名版本4测试套件,您应该会发现它非常有用——它提供了几组输入(请求)参数、正确的最终结果,也许最重要的是,它提供了中间签名步骤的结果,因此,您可以隔离实现的哪个部分没有生成正确的值,以便在后续步骤中使用。几乎根据定义,签名算法实现中的任何微小错误都会产生极不正确的结果,这使得很难确定您可能做错了什么。测试套件肯定会帮助您使代码走上正轨

V2和V4之间的显著区别在于,V4涉及创建一个签名密钥,该密钥使用您的密钥、服务、区域和日期,并且在对请求进行最终签名时使用该签名密钥,而不是直接使用密钥


乍一看,V4有点吓人,可能会让人联想到“它没有损坏,那么他们为什么要修复它?”但V4引入了安全增强功能,使其总体上成为一种更安全的机制,并且——我在这里猜测——在AWS基础设施中为您的凭证提供了增强的安全性。AWS如何在内部实现它并不是公开信息,但V4的逻辑结构表明,它可以防止单个AWS区域和服务需要拥有您的密钥。在AWS中,给定的服务只需要访问给定日期、区域和服务的签名密钥,就可以对您的请求进行身份验证,这意味着AWS似乎已经实现了V4,至少部分实现了V4,并着眼于其自身基础结构中的内部应用程序。。。这是值得称赞的。

我已经得到了签名。但我现在有另一个问题。你有什么想法吗?(请看我的更新)这几乎是一个不同的问题,但我怀疑这里的错误消息有点不准确。使用
x-amz-content-sha256:STREAMING-AWS4-HMAC-sha256-PAYLOAD
会使签名和上载过程更加复杂——而不是更少——并且要求您使用经过修改的分块传输编码形式,计算字节数并生成中间签名。看见您最好为正在上载的对象使用正确的
x-amz-content-sha256
计算正确的签名。感谢您的回复。我将尝试学习分块迁移并使用它。我不能计算一个文件的散列,因为我不知道上传开始时的文件体。我从流中读取了一个文件,但无法读取2次。amazon编写了我使用过的最糟糕的API。真是一团糟。我讨厌你。