【问题标题】:Browser uploads to s3 with instance roles浏览器使用实例角色上传到 s3
【发布时间】:2013-09-23 22:44:51
【问题描述】:

这是我的 previous question 关于使用实例配置文件签署政策文件的后续行动。

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

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

但是,一旦我的应用程序部署在 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']);

这个调用给了我一组新的凭据,但是当我使用它时它给出了与上面相同的错误。

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

【问题讨论】:

    标签: php file-upload amazon-web-services amazon-s3 amazon-ec2


    【解决方案1】:

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

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

    AWS documentation for the POST request 声明:

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

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

    【讨论】:

    • 好吧,这似乎是合理的。让我今晚开火,看看情况如何:)
    • 我已经接近了一点;它不再抱怨未知的访问ID,但现在它给出了“拒绝访问”错误:(
    • 嗯.. 尝试在 IAM 中给它s3:PutObjectAcl 权限,看看是否有帮助。
    • 做到了。我稍后会接受我自己的答案,但赏金是你提供我还没有尝试过的东西:)谢谢!
    【解决方案2】:

    正如dcro's answer 所指出的,当您使用临时凭证时,会话令牌需要传递给您正在使用的服务。 official documentation 提到了 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"
        }
      ]
    }
    

    【讨论】:

    • 嗨,杰克,请您发布有关仅使用临时凭据上传到 s3 的工作。我很难弄清楚这一点。 T__T
    • @WonderingCoder 上传本身不会在 PHP 中进行;它是在 JavaScript 中完成的,并使用 FormData 类(对于支持它的浏览器)和 CORS。这是您想了解更多的部分吗?
    • 您好,感谢您的回复。请看我的帖子,我也在使用临时凭证,我们唯一的主要区别是您使用了 SDK:my post。已经 4 天了,我仍在努力完成这项工作
    猜你喜欢
    • 2013-09-06
    • 1970-01-01
    • 1970-01-01
    • 2010-11-17
    • 2020-09-04
    • 2017-12-13
    • 1970-01-01
    • 1970-01-01
    • 2013-04-25
    相关资源
    最近更新 更多