通过使用 AWS-SDK PHP 生成的预签名帖子拒绝 AWS S3 上传访问

Posted

技术标签:

【中文标题】通过使用 AWS-SDK PHP 生成的预签名帖子拒绝 AWS S3 上传访问【英文标题】:AWS S3 Upload Access Denied through Presigned post generated with AWS-SDK PHP 【发布时间】:2019-09-28 12:47:36 【问题描述】:

我正在尝试使用 AWS SDK php 生成的预签名帖子将文件(用于我的测试的图像)上传到我的 s3 存储桶。 首先,我生成预签名的帖子,然后我使用 Postman 或通过简单的 html 表单使用给定的 PostObjectV4 数据手动创建请求...... 填写完所有内容后,请求导致访问被拒绝:-(。 与客户端关联以生成 PostObjectV4 的用户在相应的存储桶上具有 Allowed s3:PutObject 策略。

我已经尝试过:

将我的存储桶设置为公共写入,它可以工作!这表明我存在权限/政策问题...不幸的是,我的存储桶不必公开... 通过aws命令行上传文件,也可以使用

预签名后生成的 PHP 代码(数据在 $postObject 中):

$assetAwsS3Key = $this->getAssetAwsS3Key($asset);

$options = [
    ['starts-with', '$key', 'myDir/'],
];

// Optional: configure expiration time string
$expires = '+24 hours';

// Set some defaults for form input fields
$formInputs = ['acl' => 'private'];

$postObject = new PostObjectV4(
    $this->buildAwsS3UserClient(),
    $this->awsBucketName,
    $formInputs,
    $options,
    $expires
);

// Get attributes to set on an HTML form, e.g., action, method, enctype
$formAttributes = $postObject->getFormAttributes();

// Get form input fields. This will include anything set as a form input in
// the constructor, the provided JSON policy, your AWS access key ID, and an
// auth signature.
$formInputs = $postObject->getFormInputs();

return ['formAttributes' => $formAttributes, 'formInputs' => $formInputs];

我的用户(用于生成客户端)政策:


    "Version": "2012-10-17",
    "Statement": [
        
            "Effect": "Allow",
            "Action": [
                "s3:PutObject",
                "s3:GetObject",
                "s3:DeleteObject"
            ],
            "Resource": [
                "arn:aws:s3:::awsBucketName/*"
            ]
        
    ]

我用于测试上传的简单 html 表单:

<form method="post" action="https://my-bucket.s3.eu-west-3.amazonaws.com" enctype="multipart/form-data">
        <input type="hidden" name="key" value="myKey/sources/myImg.jpg" /><br />
        <input type="file"   name="file" /> <br />
        <input type="hidden" name="X-Amz-Credential" value="MYUSERACCESSKEY/20190510/eu-west-3/s3/aws4_request" />
        <input type="hidden" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" /> 
        <input type="hidden" name="X-Amz-Date" value="20190510T132109Z" />
        <input type="hidden" name="Policy" value='MYBASE64ENCODEDPOLICY' />
        <input type="hidden" name="X-Amz-Signature" value="MYSIGNATURE" />
        <input type="submit" name="submit"/>
    </form>

您是否知道为什么此上传失败,而存储桶不允许公共写入访问?

提前非常感谢!

【问题讨论】:

你有没有想过解决这个问题?我有同样的问题,很想知道答案。 嗨@JeremyGlover,请检查我对帖子的新答案,评论太长了,希望我们能帮到你,或者你已经解决了这个问题;) 【参考方案1】:

我解决了这个问题,我将分享我自第一篇文章以来所做的更改,以使上传正常工作。如有必要,请根据您的需要进行调整。

使用的 AWS 配置: - 与第一篇文章中附加到我的用户的 in-line 策略相同(请确保您在资源键中设置的存储桶名称) - 没有附加策略的 S3 存储桶 - S3 存储桶所有公共访问已禁用

这是我用于创建 postObject 的最新 php 代码:

    $objectKey = $this->objectKeyGenerator->getObjectKey($object);

    $options = [
        ['bucket' => $this->getBucketName()],
        ['eq', '$key', $objectKey],
        ['acl' => 'private']
    ];

    // Optional: configure expiration time string
    $expires = '+2 hours';

    // Set some defaults for form input fields
    $formInputs = [
        'acl' => $acl,
        'key' => $objectKey
    ];

    $postObject = new PostObjectV4(
        $this->getS3Client(),
        $this->getBucketName(),
        $formInputs,
        $options,
        $expires
    );

    // Get attributes to set on an HTML form, e.g., action, method, enctype
    $formAttributes = $postObject->getFormAttributes();

    // Get form input fields. This will include anything set as a form input in
    // the constructor, the provided JSON policy, your AWS access key ID, and an
    // auth signature.
    $formInputs = $postObject->getFormInputs();

    return ['formAttributes' => $formAttributes, 'formInputs' => $formInputs];

与我的第一篇文章相比,postObject 生成有什么变化:

我不再使用 'starts-with' 选项并手动生成对象密钥,这样上传的文件只能在生成的密钥上上传(如果给定另一个密钥,它将因为密钥非平等),但我认为这不是导致错误的原因,我很确定它始终适用于“starts-with”选项。 bucketacl 属性现在位于 $options 数组中,作为 AWS SDK for PHP 开发人员指南中给出的 Create PostObjectV4 示例代码(请参阅 [https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-presigned-post.html])。李> 在 $formInputs 数组中,存在 key,因此我可以在 postObject 中取回它(因为提交的键必须是计算出来的)。我认为这不是强制性的。

我试图通过从 $option 数组中删除 bucketacl 来重现该错误,这导致了“403 Forbidden error”,但显示一条消息“根据政策无效:额外的输入字段:bucket' 我发现不是很明显...我没有对此错误进行更多调查。

我还分享了我的 html 表单,该表单自第一次发布以来发生了一些变化,输入字段顺序发生了变化,并且由于政策要求而添加了 acl 字段。

    <form method="post" action="https://my-bucket.s3.eu-west-3.amazonaws.com/" enctype="multipart/form-data">
    <input type="hidden" name="key" value="object/key.txt" /><br />
    <input type="hidden" name="acl" value="private"/>
    <input type="hidden" name="X-Amz-Credential" value="MYUSERACCESSKEY/20190510/eu-west-3/s3/aws4_request" />      
    <input type="hidden" name="X-Amz-Algorithm" value="AWS4-HMAC-SHA256" /> 
    <input type="hidden" name="X-Amz-Date" value="20200108T093921Z" />
    <input type="hidden" name="Policy" value='MYBASE64ENCODEDPOLICY' />
    <input type="hidden" name="X-Amz-Signature" value="MYSIGNATURE" />
    File: 
    <input type="file"   name="file" /> <br />
    <!-- The elements after this will be ignored -->
    <input type="submit" name="submit"/>

最后,在我的原始帖子中,我没有谈论我的 s3 存储桶 public-accesspolicy 配置,我认为问题可能出在此处。

希望对您有所帮助,如有需要,请随时询问详情。

【讨论】:

伟大的工作。我最终让我的工作也正常工作,并使用了key 数组键与starts-with 比较,因为我想确保文件上传到我期待的确切位置。对于我的表单输入,我设置了 aclcontent-type 键。对于表单选项,我设置了aclbucketkeycontent-type 键。感谢您的跟进,我很高兴看到我们都能够继续前进。

以上是关于通过使用 AWS-SDK PHP 生成的预签名帖子拒绝 AWS S3 上传访问的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript 使用预签名 URL 保存到 S3

使用预签名的 url PUT 到 S3 会出现 403 错误

iOS 应用签名期间的预填充分发清单信息

使用带有 LSTM 的预训练 word2vec 进行单词生成

如何将文件上传到 AWS 中的预签名 URL?

整个存储桶的预签名 url