浏览器使用实例角色上传到s3

Ja͢*_*͢ck 9 php file-upload amazon-s3 amazon-ec2 amazon-web-services

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

我正在开发一个允许拖放上传到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>
Run Code Online (Sandbox Code Playgroud)

ASIAxxxyyyzzz这里的值来自实例角色凭证,从元数据服务获得; 似乎这些凭据不能在EC2之外使用,以方便基于浏览器的上传.

我也看了安全令牌服务,通过这样做生成另一组临时凭证:

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

$credentials = new Credentials($token['Credentials']['AccessKeyId'], $token['Credentials']['SecretAccessKey']);
Run Code Online (Sandbox Code Playgroud)

该调用为我提供了一组新的凭据,但在使用它时它会产生与上述相同的错误.

我希望有人之前已经这样做了,可以告诉我我错过了什么愚蠢的东西:)

dcr*_*cro 6

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

只有包含相应的安全令牌时,STS临时凭证才有效.

POST请求AWS文档指出:

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

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


Ja͢*_*͢ck 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);
Run Code Online (Sandbox Code Playgroud)

我正在使用一个辅助类来构建策略,称为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"
    }
  ]
}
Run Code Online (Sandbox Code Playgroud)