亚马逊S3上传响应HTTP 403: 访问被拒绝。

4

这将是一篇长文章,但由于我不知道哪里出了问题,我认为详细描述比省略必要信息更好。

我正在尝试按照此指南将图像上传到我的Amazon S3存储桶:https://devcenter.heroku.com/articles/s3-upload-python

我按照以下方式设置了我的代码:

index.html

<img id="preview" src="https://via.placeholder.com/320?text=Image+Upload">
<input id="file_input" class="form-control" type="file" name="image" accept="image/*">
<form method="POST" action="/gallery/upload" enctype="multipart/form-data">
    <input type="hidden" name="csrfmiddlewaretoken" value="lVE...ASG">
    <input type="hidden" id="image-url" name="image-url">
    <input class="form-control" type="text" name="title" placeholder="Title">
    <input class="form-control" type="text" name="price" placeholder="Price">
    <div class="form-control">
        <input type="checkbox" name="archive" value="True">
        <label for="archive">Archive</label>
    </div>
    <input class="form-control btn-primary" type="submit">
</form>

s3.js

function uploadFile(file, s3Data, url) {
    var xhr = new XMLHttpRequest();
    xhr.open("POST", s3Data.url);

    var postData = new FormData();
    for (key in s3Data.fields) {
        postData.append(key, s3Data.fields[key]);
    }
    postData.append('file', file);
    for (var [key, value] of postData.entries()) {
        console.log(key + ': ' + value);
    }

    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200 || xhr.status === 204) {
                document.getElementById("preview").src = url;
                document.getElementById("image-url").value = url;
            }
            else {
                alert("Could not upload file.");
            }
        }
    };
    xhr.send(postData);
}

function getSignedRequest(file) {
    var xhr = new XMLHttpRequest();
    xhr.open("GET", "/gallery/sign_s3?file_name="
                    + file.name + "&file_type=" + file.type);
    xhr.onreadystatechange = function () {
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                var response = JSON.parse(xhr.responseText);
                uploadFile(file, response.data, response.url);
            }
            else {
                alert("Could not get signed URL.");
            }
        }
    };
    xhr.send();
}

$(document).ready(function () {
    (function () {
        document.getElementById("file_input").onchange = function () {
            var files = document.getElementById("file_input").files;
            var file = files[0];
            if (!file) {
                return alert("No file selected.");
            }
            getSignedRequest(file);
        };
    })();
});

views.py

S3_BUCKET = os.environ.get('S3_BUCKET')
AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY')
s3 = boto3.client(
    's3',
    aws_access_key_id=AWS_ACCESS_KEY_ID,
    aws_secret_access_key=AWS_SECRET_ACCESS_KEY,
)

def sign_s3(request):
    file_name = request.GET.get('file_name')
    file_type = request.GET.get('file_type')

    presigned_post = s3.generate_presigned_post(
        Bucket=S3_BUCKET,
        Key=file_name,
        Fields={"acl": "public-read", "Content-Type": file_type},
        Conditions=[
            {"acl": "public-read"},
            {"Content-Type": file_type}
        ],
        ExpiresIn=3600
    )

    return JsonResponse({
        'data': presigned_post,
        'url': 'https://%s.s3.amazonaws.com/%s' % (S3_BUCKET, file_name)
    })

当我使用文件上传器选择图片时,我会收到一个警告,上面写着“无法上传文件”,同时Chrome开发者控制台会输出以下内容:
acl: public-read
Content-Type: image/jpeg
key: dragon.jpg
x-amz-algorithm: AWS4-HMAC-SHA256
x-amz-credential: AKI...TRA/20190224/us-east-2/s3/aws4_request
x-amz-date: 20190224T044659Z
policy: eyJ...XX0=
x-amz-signature: 7ea...18a
file: [object File]

似乎预签名已经完成,但我从上传中收到了这个XML响应:
<Error>
  <Code>AccessDenied</Code>
  <Message>Access Denied</Message>
  <RequestId>5D1...AB5</RequestId>
  <HostId>cYX...WKw=</HostId>
</Error>

我的CORS策略非常宽松(我计划稍后更改,但目前我想防止访问受限):

<?xml version="1.0" encoding="UTF-8"?>
<CORSConfiguration xmlns="http://s3.amazonaws.com/doc/2006-03-01/">
<CORSRule>
    <AllowedOrigin>*</AllowedOrigin>
    <AllowedMethod>GET</AllowedMethod>
    <AllowedMethod>POST</AllowedMethod>
    <AllowedMethod>PUT</AllowedMethod>
    <AllowedHeader>*</AllowedHeader>
</CORSRule>
</CORSConfiguration>

我尝试编辑存储桶策略,但仍然收到“拒绝访问”错误。这个指南帮助不大: https://aws.amazon.com/premiumsupport/knowledge-center/s3-access-denied-bucket-policy/ 我不知道是什么原因导致了这个问题,但我猜测可能是某个地方的权限问题。我不知道在哪里。任何帮助都将不胜感激。
编辑:用户的策略是AdministratorAccessAmazonS3FullAccess
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "*",
            "Resource": "*"
        }
    ]
}

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": "s3:*",
            "Resource": "*"
        }
    ]
}
编辑2:我成功地通过在公共访问设置中取消选中“阻止新的公共存储桶策略”来更改我的存储桶策略。将其设置为以下内容不再导致Bucket Policy页面上出现“访问被拒绝”的错误消息,但我仍然无法通过XHR上传文件。
{
    "Id": "Policy...237",
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt...935",
            "Action": "s3:*",
            "Effect": "Allow",
            "Resource": "arn:aws:s3:::coolwater-creations/*",
            "Principal": {
                "AWS": [
                    "...764"
                ]
            }
        }
    ]
}

是的,我正在从控制台进行操作。有一个用户,只应用了“AdministratorAccess”策略。我现在已经附加了“AmazonS3FullAccess”策略,但是无论是从XHR还是编辑存储桶策略都仍然收到访问被拒绝的错误提示。 - Matt McCarthy
我已将它们添加到问题中。具体来说,您想要哪些控制台截图? - Matt McCarthy
你能添加整个策略吗?即:{ "Version": "2012-10-17", "Statement": { "Effect": "Allow", "Action": "service-prefix:action-name", "Resource": "*", "Condition": { "DateGreaterThan": {"aws:CurrentTime": "2017-07-01T00:00:00Z"}, "DateLessThan": {"aws:CurrentTime": "2017-12-31T23:59:59Z"} } } }如果您添加IAM控制台区域的屏幕截图(隐藏任何敏感信息,但保留用户名),以及S3错误的屏幕截图,可能会有所帮助。 - Naguib Ihab
1
https://imgur.com/a/eqsZ1MN - Matt McCarthy
1
截图无法阅读,但可能的解释是您的存储桶配置了多个选项,不允许公共访问,这可能会阻止您上传具有 public-read 权限的文件,并且也可能会防止您设置过于宽松的策略。请参考 https://aws.amazon.com/blogs/aws/amazon-s3-block-public-access-another-layer-of-protection-for-your-accounts-and-buckets/ 了解更多信息。 - Michael - sqlbot
显示剩余4条评论
1个回答

0

我怀疑您登录的账户与IAM中所看到的账户不同:

从您分享的截图中可以看出,IAM用户的姓名与您的用户名不匹配。

enter image description here

如果您已登录该IAM用户,则应在用户名中看到相同的名称

enter image description here

所以你要么作为 root 账户登录,这种情况下错误将在存储桶创建方面(请检查 https://aws.amazon.com/premiumsupport/knowledge-center/s3-troubleshoot-403/)。 要么你正在使用 AWS 合作伙伴的 SSO 登录,在这种情况下,你的权限会略有不同,并且不受你在那里看到的 IAM 的控制。


我相信我已经在控制台中作为root账户登录,但是我不知道我使用SDK登录的是哪个账户。我使用了我的AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY进行登录。我也将它们放在了我的.aws/config文件中,但我认为那是用于CLI的。 - Matt McCarthy
我该从这里做什么?很遗憾,你提供的链接没有帮助。 - Matt McCarthy
它怎么不起作用了?你是否按照故障排除步骤进行了操作? - Naguib Ihab
我已经做了。规范的ID匹配,我的Bucket策略中没有任何冲突的拒绝语句,我已经验证了用于访问S3的用户凭证,我没有使用EC2实例,因为我正在尝试进行POST而不是GET,我不用担心KMS加密,请求者付费未启用,我也没有使用AWS组织。 - Matt McCarthy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接