You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用EC2 IAM角色生成S3预签名URL上传时遇InvalidAccessKeyId错误

问题:使用EC2 IAM角色生成预签名URL上传S3时提示InvalidAccessKeyId错误

尝试通过预签名URL向私有S3存储桶上传文件时,触发以下错误:

<Error>
    <Code>InvalidAccessKeyId</Code>
    <Message>The AWS Access Key Id you provided does not exist in our records.</Message>
    <AWSAccessKeyId></AWSAccessKeyId>
    <RequestId></RequestId>
    <HostId></HostId>
</Error>

EC2服务器已关联具备以下权限的IAM角色:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:*",
                "s3-object-lambda:*",
                "kms:*"
            ],
            "Resource": "*"
        }
    ]
}

该角色的信任关系配置:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "Service": "ec2.amazonaws.com"
            },
            "Action": "sts:AssumeRole"
        }
    ]
}

业务流程

  1. 前端调用PHP接口获取预签名上传所需参数:
$s3Client = new Aws\S3\S3Client([
    'version'     => '2006-03-01',
    'region'      => $region
]);

$formInputs = ['acl' => 'private'];

$options = [
    ['acl' => 'private'],
    ['bucket' => $_ENV['AWS_S3_BUCKET']],
    ['starts-with', '$key', $pth],
];

$expires = '+1 hours';

$postObject = new \Aws\S3\PostObjectV4(
    $s3Client,
    $_ENV['AWS_S3_BUCKET'],
    $formInputs,
    $options,
    $expires
);

$fattr = $postObject->getFormAttributes();
$finput = $postObject->getFormInputs();
  1. 前端通过Ajax提交表单到S3:
var fd = new FormData();
fd.append("acl", fp["acl"]);
fd.append("key", fp['Path']);
fd.append("X-Amz-Credential", fp["X-Amz-Credential"]);
fd.append("X-Amz-Algorithm", fp["X-Amz-Algorithm"]);
fd.append("X-Amz-Date", fp["X-Amz-Date"]);
fd.append("Policy", fp["Policy"]);
fd.append("X-Amz-Signature", fp["X-Amz-Signature"]);
fd.append('file', file);

$.ajax({
    url: fp['action'],
    type: 'post',
    data: fd,
    cache: false,
    contentType: false,
    processData: false,
})...

原硬编码AWS凭证时功能正常,切换为EC2 IAM角色后出现上述错误,以下是解决思路:

  • 验证EC2实例是否正确获取IAM角色临时凭证:
    登录EC2实例,执行curl http://169.254.169.254/latest/meta-data/iam/security-credentials/查看是否返回关联的角色名称;再访问http://169.254.169.254/latest/meta-data/iam/security-credentials/[角色名称],确认返回包含AccessKeyId、SecretAccessKey、SessionToken的有效临时凭证。如果无法获取,检查实例是否正确关联IAM角色,或者重启实例刷新凭证。

  • 检查AWS PHP SDK是否自动获取IAM角色凭证:
    确保PHP SDK版本支持实例角色凭证自动获取(AWS SDK for PHP v3默认支持),代码中不要显式设置credentials参数。如果SDK没有自动获取,可能是实例元数据访问受限,或者SDK配置被覆盖。可以在初始化S3Client时添加'credentials' => new CredentialProvider()强制使用默认凭证链,或者检查环境变量是否残留旧的硬编码凭证(比如AWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEY)。

  • 检查PostObjectV4生成的凭证是否包含SessionToken:
    使用IAM角色生成预签名POST表单时,需要在表单参数中包含X-Amz-Security-Token(对应临时凭证的SessionToken)。查看$finput是否包含该字段,如果没有,说明SDK没有正确处理临时凭证,需要手动添加:

    // 获取当前凭证
    $credentials = $s3Client->getCredentials()->wait();
    if ($credentials->getSecurityToken()) {
        $finput['X-Amz-Security-Token'] = $credentials->getSecurityToken();
    }
    

    前端也需要将该字段添加到FormData中:

    fd.append("X-Amz-Security-Token", fp["X-Amz-Security-Token"]);
    
  • 验证S3存储桶的CORS配置:
    确保存储桶CORS规则允许POST请求,并且允许携带必要的请求头(包括x-amz-security-token)。典型的CORS配置示例:

    [
        {
            "AllowedHeaders": ["*"],
            "AllowedMethods": ["POST"],
            "AllowedOrigins": ["你的前端域名"],
            "ExposeHeaders": []
        }
    ]
    

内容的提问来源于stack exchange,提问作者barneyAgumble

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.01 23:00:27