Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AWS S3 Upload Access Denied through Presigned post generated with AWS-SDK PHP

i'm trying to upload a file (an image for my tests) to my s3 bucket with a pre-signed post generated with AWS SDK PHP. Firstable i generate the pre-signed post, then i manually create the request with given PostObjectV4 datas with Postman or via a simple html form... After filling everything, the request result in Access Denied :-(. The user associated with the client to generate the PostObjectV4 has Allowed s3:PutObject policy on the corresponding bucket.

I've already tried to :

  • Set my bucket as public write, and it works ! This indicates me a problem of permission / policy... Unfortunately my bucket don't have to be public...
  • Upload a file via aws command line, it works too

PHP code of pre-signed post generation (datas are in $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];

My user (used for client generation) policy :

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

My simple html form for test upload:

<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>

Do you have an idea/clue why this upload fail while the bucket isn't public write-access allowed ?

Many thanks in advance !

like image 477
ffouillet Avatar asked Oct 15 '22 15:10

ffouillet


1 Answers

I got rid of the problem, i'll share the changes i made here since my first post in order to get the upload to work. Please adapt it to your needs if necessary.

The AWS configuration used: - Same in-line policy attached to my user as in the first post(please be sure of the bucket name you set in the resource key) - S3 Bucket with no policy attached to it - S3 Bucket All Public Access Disabled

Here is my up to date php code for creating the postObject :

    $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];

What changes in the postObject generation compared to my first post :

  • I did not use 'starts-with' option anymore and generate the object key manually so that the uploaded file will have to be uploaded only at the generated key (if another key is given it will fail because of the key non-equality), but i think this is was not what was leading to the error and i'm pretty sure it will always work with 'starts-with' option.
  • bucket and acl attributes are now in the $options array as the Create PostObjectV4 sample code given in the AWS SDK for PHP Developer Guide (see [https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/s3-presigned-post.html]).
  • In the $formInputs array, key is present, so that i can get it back in the postObject (because the key submitted must be the one calculated). I think this is not mandatory.

I tried to reproduce the error by removing the bucket and acl from the $option array and this led to an '403 Forbidden error' but with a message stating 'Invalid according to Policy: Extra input fields: bucket' which i find not very obvious... I haven't investigate more on this error.

I'm also sharing my html form that changed a bit since first post, input fields order changed and acl field was added because required by the policy.

    <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"/>

To conclude, in my original post i don't talk about my s3 bucket public-access and policy configuration and i think that the problem could have been here.

Hope this helps, feel free to ask for details if necessary.

like image 117
ffouillet Avatar answered Oct 18 '22 09:10

ffouillet