9

这是我之前关于使用实例配置文件签署策略文档的问题的后续内容。

我正在开发一个允许直接拖放上传到 S3 存储桶的系统;首先向包含文件元数据的服务器发出 AJAX 请求。验证后,我的服务器会使用用于完成上传的表单参数进行响应。

设置基于浏览器的上传的过程在这里得到了很好的解释,并且在我的本地测试环境中一切正常。

但是,一旦我的应用程序部署在 EC2 实例上,当浏览器尝试上传文件时,我会看到此错误:

<Error>
    <Code>InvalidAccessKeyId</Code>
    <Message>The AWS Access Key Id you provided does not exist in our records.</Message>
    <RequestId>...</RequestId>
    <HostId>...</HostId>
    <AWSAccessKeyId>ASIAxxxyyyzzz</AWSAccessKeyId>
</Error>

这里的值ASIAxxxyyyzzz来自于从元数据服务获取的实例角色凭证;似乎这些凭据不能在 EC2 之外用于促进基于浏览器的上传。

我还查看了Security Token Service以通过执行以下操作生成另一组临时凭据:

$token = $sts->assumeRole(array(
    'RoleArn' => 'arn:aws:iam::xyz:role/mydomain.com',
    'RoleSessionName' => 'uploader',
));

$credentials = new Credentials($token['Credentials']['AccessKeyId'], $token['Credentials']['SecretAccessKey']);

该调用为我提供了一组新凭据,但是当我使用它时,它给出了与上述相同的错误。

我希望有人以前做过这个并且可以告诉我我错过了什么愚蠢的事情:)

4

2 回答 2

6

AWS 文档对此非常困惑,但我怀疑您需要x-amz-security-token在 S3 上传 POST 请求中包含该参数,并且其值与SessionToken您从 STS ( $token['Credentials']['SessionToken']) 获得的值相匹配。

STS 临时凭证仅在您包含相应的安全令牌时才有效。

POST 请求的AWS 文档指出:

每个使用 Amazon DevPay 的请求都需要两个 x-amz-security-token 表单字段:一个用于产品令牌,一个用于用户令牌。

但该参数也在 DevPay 之外使用,用于传递 STS 令牌,您只需在表单字段中传递一次。

于 2013-09-26T09:14:31.493 回答
3

正如dcro 的回答所指出的,当您使用临时凭证时,会话令牌需要传递给您正在使用的服务。官方文档中提到了该字段x-amz-security-token,但似乎暗示它仅用于 DevPay;这可能是因为 DevPay 使用相同类型的临时凭证,因此需要会话安全令牌。

2013-10-16 : 亚马逊更新了他们的文档以使其更加明显。

事实证明,甚至根本不需要使用 STS。元数据服务收到的凭据也带有这样的会话令牌。当 SDK 与临时凭证一起使用时,此令牌会自动为您传递,但在这种情况下,最终请求是由浏览器发出的,因此需要显式传递。

以下是我的工作代码:

$credentials = Credentials::factory();
$signer = new S3Signature();
$policy = new AwsUploadPolicy(new DateTime('+1 hour', new DateTimeZone('UTC')));

$policy->setBucket('upload.mydomain.com');
$policy->setACL($policy::ACL_PUBLIC_READ);
$policy->setKey('uploads/test.jpg');
$policy->setContentType('image/jpeg');
$policy->setContentLength(5034);

$fields = array(
    'AWSAccessKeyId' => $credentials->getAccessKeyId(),
    'key' => $path,
    'Content-Type' => $type,
    'acl' => $policy::ACL_PUBLIC_READ,
    'policy' => $policy,
);

if ($credentials->getSecurityToken()) {
    // pass security token
    $fields['x-amz-security-token'] = $credentials->getSecurityToken();
    $policy->setSecurityToken($credentials->getSecurityToken());
}

$fields['signature'] = $signer->signString($policy, $credentials);

我正在使用一个辅助类来构建策略,称为AwsUploadPolicy; 在撰写本文时,它还不完整,但它可能会帮助其他有类似问题的人。

权限是最后一个问题。我的代码将 ACL 设置为public-read,这样做需要额外的s3:PutObjectAcl权限。

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "s3:PutObject",
        "s3:PutObjectAcl"
      ],
      "Sid": "Stmt1379546195000",
      "Resource": [
        "arn:aws:s3:::upload.mydomain.com/uploads/*"
      ],
      "Effect": "Allow"
    }
  ]
}
于 2013-10-01T00:06:00.973 回答