IAM策略绑定角色后S3列表权限控制失效问题排查
我来帮你拆解下问题的核心,以及对应的解决办法:
问题出在哪?
你的IAM策略本身逻辑是对的,但你可能误解了S3权限控制的工作方式:S3的ListBucket权限的Condition是校验你发送请求时携带的s3:prefix参数,而不是自动帮你过滤返回的结果。
当你的代码调用s3.listObjectsV2({Bucket: 'my-bucket'})时,没有指定Prefix,相当于请求的是桶根目录下的所有内容。这时候策略里的"s3:prefix": [ "", "home/", "home/BOB/*" ]条件会匹配空字符串,IAM会允许这个请求,S3自然就返回了home/下的所有文件夹(ALICE、BOB、MARY),这就是为什么你能看到所有目录的原因。
另外,硬编码BOB也不是长久之计,没法适配不同用户,我们需要让策略动态关联Web Identity中的用户身份。
解决方案步骤
1. 修改应用代码,动态传递用户专属Prefix
首先,你需要从Web Identity Token(也就是req.body.id_token)中解析出当前用户的标识(比如用户名、sub字段等,取决于你的身份提供商返回的声明),然后在调用listObjectsV2时指定对应的Prefix,同时加上Delimiter: '/'来只返回目录结构,避免返回所有文件。
示例代码(需要先安装jsonwebtoken包):
const jwt = require('jsonwebtoken'); // 解析id_token获取用户标识,这里假设id_token里有`username`字段是用户名 const decodedToken = jwt.decode(req.body.id_token); const username = decodedToken.username; // 可根据实际声明调整,比如用decodedToken.sub var params = { DurationSeconds: 3600, RoleArn: "arn:aws:iam::role/my_test_role", RoleSessionName: `session_${username}`, // 用用户名做会话名更清晰 WebIdentityToken: req.body.id_token }; sts.assumeRoleWithWebIdentity(params, function(err, data) { if (err) { console.error('Assume role error:', err); return; } // 创建带临时凭证的S3客户端 const s3 = new AWS.S3({ credentials: { accessKeyId: data.Credentials.AccessKeyId, secretAccessKey: data.Credentials.SecretAccessKey, sessionToken: data.Credentials.SessionToken } }); // 调用listObjectsV2时指定用户专属Prefix和Delimiter s3.listObjectsV2({ Bucket: 'my-bucket', Prefix: `home/${username}/`, Delimiter: '/' }, (listErr, listData) => { if (listErr) { console.error('List objects error:', listErr); return; } console.log('User-specific folders:', listData.CommonPrefixes); // 这里返回的就是当前用户的专属目录内容 }); });
2. 修改IAM策略,动态关联用户身份
把策略中硬编码的BOB改成从Web Identity声明中获取的变量,这样同一个策略可以适配所有用户。假设你的id_token里有username声明,就用${oidc:claim/username};如果是用Cognito身份池,可能用${cognito-identity.amazonaws.com:sub},具体取决于你的身份提供商返回的声明键。
修改后的策略示例:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:ListAllMyBuckets", "s3:GetBucketLocation" ], "Resource": "arn:aws:s3:::*" }, { "Effect": "Allow", "Action": "s3:ListBucket", "Resource": "arn:aws:s3:::my_bucket", "Condition": { "StringLike": { "s3:prefix": [ "", "home/", `home/${oidc:claim/username}/*` ] } } }, { "Effect": "Allow", "Action": "s3:*", "Resource": [ `arn:aws:s3:::my_bucket/home/${oidc:claim/username}`, `arn:aws:s3:::my_bucket/home/${oidc:claim/username}/*` ] } ] }
注意:要确保你的IAM角色信任策略中,已经正确配置了Web Identity提供商,并且允许对应的身份来扮演该角色。不同身份提供商(比如Auth0、Cognito)返回的声明字段可能不同,需要根据实际情况调整
oidc:claim/xxx中的键名。
3. 验证逻辑
当用户BOB访问时,应用会解析出username=BOB,调用listObjectsV2时传递Prefix: 'home/BOB/',IAM策略的Condition会匹配home/BOB/*,允许该请求,S3返回home/BOB/下的内容;如果用户尝试修改Prefix为home/ALICE/,IAM策略会拒绝该请求,因为home/ALICE/*不在允许的prefix列表中,从而实现权限隔离。
关键提醒
IAM策略是控制请求是否被允许,而不是过滤返回的结果。所以一定要确保应用发送的请求本身就只请求用户有权限访问的资源,这样才能达到预期的权限控制效果。
内容的提问来源于stack exchange,提问作者Jas Ahluwalia

