资讯动态

云上安全访问控制:STS临时凭证与角色扮演实战指南

发布时间:2026/10/14 3:28:10 来源:尧图企业网站定制
1. 先从“为什么要用STS”说起1.1 一把“万能钥匙”放在手里真的安全吗如果你做过云上资源管理大概率遇到过这种场景服务器上要跑一个脚本定期把数据备份到对象存储或者要给第三方合作伙伴开放某个目录的读取权限再或者公司内部不同部门之间要互相访问对方账号下的资源。最早的方案很简单——创建一个长期密钥也就是AccessKey ID和AccessKey Secret把这对钥匙放在配置文件里脚本调用接口时带上它完事。听起来没什么问题但实际用起来你会慢慢发现几件让人头疼的事第一长期密钥一旦泄露除非你手动吊销否则别人就能一直用它访问你的资源。而你往往要等到账单异常或者审计日志里出现可疑调用时才能发现那个时间窗口可能已经过去很久了。第二每个场景都用同一把钥匙权限根本没法细分。运维脚本要读对象存储数据分析任务要读数据库日志采集要写日志服务如果全用同一个AK那这个AK的权限只能取所有需求的并集——而这恰恰是最危险的做法因为攻击者拿到它就等于拿到了整个项目组的全部家当。第三跨账号授权非常别扭。你要给另一个账号的人开权限要么把钥匙直接给对方太可怕了要么在两边同时配置复杂的授权关系维护成本很高。STS就是为了解决这些问题出现的。它的全称是Security Token Service中文一般叫安全令牌服务核心作用是帮你签发临时凭证——一组有时效、有范围、可以随时撤销的访问凭证。相比长期AK/SK它最大的差别在于临时凭证默认就是“最小权限”和“最短时效”的设计思路泄露了也不怕几分钟或几小时后自动失效不用你手动去吊销。这篇文章我会从概念拆到实操把STS是什么、角色和信任策略怎么理解、临时凭证怎么获取和使用、踩过哪些坑一次性讲清楚。适合刚接触云上权限管理的开发者也适合已经在用STS但某些细节没想明白的运维和架构师。1.2 临时凭证的设计思路像酒店房卡不像家门钥匙为了理解STS的价值我建议你先忘掉AK/SK那套东西想一个生活场景你住酒店前台给你一张房卡。这张房卡只能刷开你住的那间房只能在你住的那几天有效过期自动失效如果你提前退房前台随时可以把它作废。相比之下你家的大门钥匙——只要配上就一直有效能开你家门三十年。长期AK/SK就是你家的门钥匙而STS签发的临时凭证就是那张酒店房卡。它有三个特征恰好都是安全上最渴求的时效性凭证有一个有效期默认通常几十分钟到几小时最长一般不超过12小时不同云平台的上限略有差别到期后自动失效不需要你去回收。限定性签发凭证时可以通过额外参数进一步限定它的权限比如只能读某个存储桶的某个目录只能调用某个API。可追溯性临时凭证通常绑定一个角色身份审计日志里能看到是谁、通过什么角色、在什么时间做了哪些操作比一把共享的长期AK清晰得多。所以你会发现STS本质上是把“身份”和“访问凭证”拆开了。长期来看每个人员、每个应用应该有一个稳定的身份通常是RAM用户或角色但真正用来调API的凭证可以是动态签发、用完即弃的。这个思路后来也被容器、Serverless等场景吸收基本上成了云上访问控制的主流范式。1.3 哪些场景必须用STS哪些场景不必用不用STS取决于你的业务是否有“身份边界”和“时效边界”。我整理了三个最常见的必须用STS的场景第一个是跨账号访问。比如你的公司有多个云账号生产环境一个账号运维平台在另一个账号运维平台需要调用生产账号的ECS API来执行一些操作。这种场景下标准的做法是在生产账号里创建一个角色在信任策略里允许运维账号扮演它然后运维账号通过STS的AssumeRole接口拿到生产账号的临时凭证。整个过程生产账号的长期AK完全没有离开自己的账号边界。第二个是应用/服务器临时授权。比如EC2实例或者容器需要访问对象存储、数据库你不想把AK写死在配置文件里更不想塞进镜像。可以通过“实例绑定角色”的方式让实例在启动时自动通过元数据服务获取临时凭证。这种模式的好处是就算有人把整个镜像拖走了镜像里也没有长期密钥那个角色只在实例存活期间有效。第三个是联邦身份认证。公司有自己的身份系统比如企业内部的统一登录认证员工登录内部系统后通过SAML断言或者OIDC令牌换取云上的临时凭证这样员工不需要单独记住一套云账号密码财务审计也能统一到公司身份体系里。这种场景对应STS里的AssumeRoleWithSAML和AssumeRoleWithWebIdentity接口。当然不是说所有场景都要上STS。如果你只是本地调试代码临时调一两个API直接用自己的RAM用户AK也完全OK。STS更适合正式环境、自动化流程、第三方授权这些“需要控制边界”的地方。用我自己的话说能不给长期钥匙就不给能给短时钥匙就绝不给长期钥匙。2. 拆开STS角色、信任策略和权限策略2.1 角色不是“人”而是一个“可以被扮演的身份”很多初学者第一次接触STS时最容易糊涂的地方是角色Role和用户User到底有什么区别简单说用户是“谁”角色是“什么身份”。RAM用户有固定的长期密钥代表一个具体的操作者人或者应用而角色没有长期密钥它自己本身不能登录也不能直接调用API它只是一个“身份容器”里面写好了“你能干什么”的权限策略。谁想使用这个身份得先经过“扮演”Assume的动作被允许扮演并成功扮演后才拿到临时凭证。我习惯把一个角色理解为一件带门禁权限的工作服。工作服本身放在那里谁也不能拿它去刷门禁但你如果把某件工作服指定给某人穿他穿上之后就能进出特定区域脱下来就没权限了。角色就是那件工作服信任策略规定“谁能穿”权限策略规定“穿上能去哪”。角色可以同时被多个主体扮演吗可以。比如一个“日志读取角色”运维同事可以扮演它去排查问题监控系统也可以扮演它去拉取日志。只要他们都符合信任策略的条件都能通过扮演获得各自的临时凭证。这比给每个人单独配长期AK要好管理得多。2.2 信任策略谁有资格敲这道门信任策略Trust Policy是角色里最关键的一环它写在角色上决定了谁可以Assume这个角色。默认情况下新创建的角色信任策略是空的——谁都不能扮演。你需要明确指定信任方通常是某个RAM用户、某个RAM角色或者某个云服务。比如你创建了一个“跨账号只读角色”信任策略里就要写明允许哪个账号的哪个用户来扮演。用JSON表达大概是这种感觉{ Statement: [ { Effect: Allow, Principal: { RAM: acs:ram::1234567890123456:user/ops }, Action: sts:AssumeRole } ] }这里面的Principal主体就是“允许谁来”Action固定是AssumeRole有时候还会加一个Condition条件来进一步约束比如限制来源IP、限制请求必须使用HTTPS、限制MFA已校验等。信任策略是安全检查的第一道大门也是最容易出错的地方。我见过不少线上事故原因就是信任策略写得过于宽松比如Principal写成了“所有用户”或者忘了加Condition限制导致一个本该只给运维人员的角色变成了整个账号都能扮演。所以我的建议是信任策略永远遵循最小化原则只写明确的账号ID和用户名字能用Condition收紧的尽量收紧。2.3 权限策略拿到令牌之后能干什么如果说信任策略是“能不能进门”权限策略就是“进门之后能碰哪些东西”。权限策略是角色的另一个核心属性它决定了通过这个角色签发出来的临时凭证最终能调用哪些云产品的哪些API。权限策略也写成JSON理解起来和RAM策略完全一致。比如这个角色只能读取某个存储桶{ Statement: [ { Effect: Allow, Action: oss:GetObject, Resource: [acs:oss:*:*:bucket/my-bucket/*] } ] }关于权限策略有两点我觉得值得强调第一如果角色没有配权限策略就算能成功扮演、拿到了STS令牌调用API也照样报“AccessDenied”。因为令牌本身没有权限权限是角色赋予的。这就好比酒店房卡能开了房门但房间里的小冰箱需要额外付费刷一次权限策略决定的就是“能刷开哪一层”。第二扮演角色时还可以再传一个“会话策略”Session Policy在角色的权限范围之内进一步缩小本次会话的权限。这个机制很实用比如同一个“数据分析角色”权限比较大但某次临时任务只需要读一份报告那就可以在调用AssumeRole时传一个只允许读那个报告的会话策略让本次签发的令牌权限比角色默认权限更小。这是一种很优雅的“动态收缩权限”手法后面实操部分我会专门演示。2.4 临时凭证三件套和你熟悉的AK/SK有什么区别通过STS获取到的临时凭证由三部分组成临时AccessKeyId、临时AccessKeySecret以及一个SecurityToken。很多人第一次看到这个结构会觉得奇怪思前想后不还是“一对钥匙加一个token”吗实际上这个SecurityToken是关键它里面携带了这次会话的身份元数据角色ID、权限边界、有效期、签发方式等云服务在验签API请求时会校验这个Token和临时AK是否配套以及是否还在有效期内。临时AK/SK和长期AK/SK在使用方式上几乎一样——签名过程、Header拼接、SDK配置方式都是同一套逻辑。你只需要把SecurityToken也配置进去即可。区别在于临时AK/SK的有效期短到期后必须重新获取临时AK/SK没有独立的身份ID它必须和SecurityToken一起使用才有效临时AK/SK无法用于控制台登录、无法管理某些账号级别的操作。我用一张表总结临时凭证和长期凭证的关键差异方便你对照对比维度长期AK/SKSTS临时凭证有效期通常长期有效最长几年默认1小时最长一般不超过12小时泄露风险高必须手动吊销低短时失效权限范围归属于用户范围固定归属于角色可在会话中收缩跨账号授权困难需要对方持有密钥天然支持通过角色信任完成审计追溯对应用户固定对应用户角色会话可区分每一次扮演是否可登录控制台可以不可以3. 完整实操创建角色、扮演角色、使用临时凭证3.1 前置准备先确认你的账号和服务状态在动手之前有几个前置条件得确认不然中途容易卡壳。你的账号得有创建RAM角色和策略的权限。一般账号的AdministratorAccess天然具备如果你是子用户需要确认有没有ram:CreateRole、ram:CreatePolicy、ram:AttachPolicyToRole这些权限。如果没有找管理员开通或者让管理员直接帮你操作。第二个是确认你要访问的云产品支持STS令牌。绝大多数云产品都支持但个别比较老的产品或部分控制台功能可能不支持临时凭证访问。最稳妥的办法是查一下目标产品的官方文档或者直接在生产环境里用最小权限试调一次。第三个是确定你要用哪种方式调用STS。日常调试用CLI最快写业务代码用SDK图形界面操作用控制台。我下面的演示会覆盖后两种你可以根据自己的场景跳着看。3.2 创建角色并配置好信任策略控制台步骤我以常见的“允许账号A的用户ops扮演账号B的只读角色”为例演示控制台操作流程。第一步在目标账号账号B的RAM控制台里选择“角色”点击“创建角色”。类型选择“RAM角色”这一步很重要——如果选成“云服务角色”通常只能授权给云服务本身没法授权给其他的RAM用户。第二步填写角色名称比如叫“cross-account-readonly”在信任策略配置框里粘贴事先准备好的JSON。核心是这个Principal{ Statement: [ { Effect: Allow, Principal: { RAM: [ acs:ram::1234567890123456:user/ops ] }, Action: sts:AssumeRole, Condition: { StringEquals: { sts:ExternalId: project-x-2024 } } } ] }这里我用了一个Condition要求调用方必须携带正确的ExternalId。ExternalId是一个自定义字符串用来防止“混淆代理人攻击”。举个例子你的合作伙伴要用你的资源如果只写死对方账号ID对方账号内部万一有个被攻破的子用户它也能扮演你的角色。加上ExternalId之后对方服务端必须知道你们约定好的这个字符串才行安全性高很多。第三步给角色添加权限策略。控制台里可以直接选系统策略比如“AliyunOSSReadOnlyAccess”只读访问对象存储也可以选自己的自定义策略。我建议正式环境用自定义策略把Action和Resource都收窄别图省事直接上系统管理权限。第四步保存并记住角色的ARN。ARN是云上资源的全球唯一标识形如acs:ram::1234567890123456:role/cross-account-readonly。后面所有扮演操作都需要用到这个ARN。3.3 扮演角色获取临时凭证Python SDK示例创建好角色之后接下来就是在账号A这边写代码调用STS接口去扮演角色拿到临时凭证。以Python为例用某云厂商的SDK可以这样写from alibabacloud_sts20150401.client import Client as StsClient from alibabacloud_sts20150401 import models as sts_models from alibabacloud_tea_openapi.models import Config # 使用账号A的长期AK配置STS客户端 config Config( access_key_idLTAIxxxxxxx, access_key_secretxxxxxxxxxxxxxx, region_idcn-hangzhou ) client StsClient(config) # 构造AssumeRole请求 req sts_models.AssumeRoleRequest( role_arnacs:ram::1234567890123456:role/cross-account-readonly, role_session_nameops-session-001, duration_seconds3600, external_idproject-x-2024 ) resp client.assume_role(req) credentials resp.body.credentials print(临时AK:, credentials.access_key_id) print(临时SK:, credentials.access_key_secret) print(Token:, credentials.security_token) print(过期时间:, credentials.expiration)这里有几个参数需要特别解释一下role_session_name是会话名称用来标记这一次扮演是给谁用的。它在审计日志里非常关键建议命名规则带上用途和人名比如ops-zhangsan-20240501。如果不指定很多云平台默认会生成一个随机值排查问题的时候你就看不出是谁在调用了。duration_seconds是会话有效期单位是秒。不同平台对这个值有上下限限制常见的最小值是90015分钟最大值从3600到43200不等。不求最长只求够用。像这种交互式脚本默认3600就够了如果是晚上定时任务怕任务中途token过期可以适当调长但千万别调成系统允许的上限因为有效期越长泄露后的风险窗口越大。external_id是信任策略里约定好的那个字符串必须和角色Trust Policy中的Condition匹配否则扮演会被拒绝。有些场景可能不需要ExternalId那这两个地方就都不写。成功之后返回体里除了凭证三件套还有一个expiration时间建议代码里判断一下在过期前提前刷新。比如你写一个函数缓存临时凭证当发现剩余时间小于5分钟时主动重新调用AssumeRole。3.4 使用临时凭证CLI与SDK场景拿到临时凭证之后接下来就是怎么用它的问题。CLI场景最简单在shell里设置三个环境变量export ALIBABA_CLOUD_ACCESS_KEY_ID临时AK export ALIBABA_CLOUD_ACCESS_KEY_SECRET临时SK export ALIBABA_CLOUD_SECURITY_TOKENSecurityToken设置完后直接用原来的CLI命令就行比如aliyun oss ls oss://my-bucket/data/ --access-key-id $ALIBABA_CLOUD_ACCESS_KEY_ID --access-key-secret $ALIBABA_CLOUD_ACCESS_KEY_SECRET --security-token $ALIBABA_CLOUD_SECURITY_TOKEN如果你的CLI版本支持从环境变量读取配置甚至可以不用加参数直接aliyun oss ls ...就能跑通。CLI配置无误的标志是返回正常的数据列表如果报错大概率是SecurityToken没设置进去或者AccessKeyId和Secret不是同一对。SDK场景也很简单以Python访问对象存储为例初始化客户端时把临时凭证的三件套传进去即可import oss2 auth oss2.StsAuth( temp_access_key_id临时AK, temp_access_key_secret临时SK, security_tokenSecurityToken ) bucket oss2.Bucket(auth, https://oss-cn-hangzhou.aliyuncs.com, my-bucket) print(bucket.list_objects(prefixdata/).objects)注意这套代码和用长期AK初始化客户端几乎一样只是换成了StsAuth多传一个security_token。你甚至可以封装一个函数先调上一节的AssumeRole再返回一个已经初始化好的bucket对象。这样业务代码里根本不会出现临时凭证的概念只需要调一个函数就能完成“扮演角色→访问资源”的全过程。3.5 会话策略在角色权限之上继续收缩这部分是我非常推荐实际项目里使用的技巧扮演角色时传一个policy参数在角色原本的权限范围里再砍一刀。举个例子你的“数据分析角色”默认允许读写某个目录下所有文件但某条流水线只需要读取其中某个子目录你不想为这个子目录单独创建一个角色就可以在AssumeRole时传入一个会话策略把本次会话的权限限制到子目录req sts_models.AssumeRoleRequest( role_arnacs:ram::1234567890123456:role/cross-account-readonly, role_session_namepipeline-session, duration_seconds1800, policy{Statement:[{Effect:Allow,Action:oss:GetObject,Resource:acs:oss:*:*:bucket/my-bucket/data/report-2024/*}]} )这个policy字段的作用是最终生效的权限 角色权限策略 ∩ 会话策略。也就是说会话策略不能放大权限只能缩小权限。这个机制特别适合“一个角色多种用途”的场景让临时凭证的权限粒度进一步变细。我实际项目中用的比较多的一个组合是角色本身只配一个“基础目录读权限”然后每次运行时通过会话策略精确指定本次任务要读的路径从源头上避免一个任务不小心读到另一个任务的数据。4. 三种接入方式的选择建议4.1 控制台操作适合初始化与排查控制台适合角色创建、权限策略编写、信任策略调整这类“低频管理型”操作。大部分云平台的控制台都提供了角色扮演功能有些还可以直接在网页上模拟扮演快速验证角色的权限配置是否符合预期。控制台操作的好处是所见即所得策略JSON直接高亮提示语法错误信任策略的Principal也能按照图形界面选择不容易写错。缺点是不适合频繁操作因为你不可能每次跑任务都去网页上点一遍。4.2 CLI与脚本适合日常调试和自动化任务CLI是日常调试的好帮手。一条命令就能完成AssumeRole而且返回JSON可以直接用jq解析出临时凭证再配合shell脚本就能实现一套“自动换令牌”的流程。比如写一个脚本先扮演角色然后把临时凭证导出为环境变量再执行后续所有云命令。# 获取临时凭证并导出环境变量 eval $(python assume_role.py | python -c import sys,json; djson.load(sys.stdin); print(fexport ALIBABA_CLOUD_ACCESS_KEY_ID{d[\AccessKeyId\]}); print(fexport ALIBABA_CLOUD_ACCESS_KEY_SECRET{d[\AccessKeySecret\]}); print(fexport ALIBABA_CLOUD_SECURITY_TOKEN{d[\SecurityToken\]}))这套方式特别适合Cron定时任务。每天的备份脚本可以先换一次临时凭证比如设置8小时有效期然后放心去跑到期前如果任务还没结束脚本里加个判断让它在有效期过半时重新换一次就能无缝衔接。4.3 SDK集成适合业务系统与自动化平台如果你在写正式的自动化平台、数据同步程序、CI/CD流水线这类长期运行的系统强烈建议直接在代码里集成SDK。因为SDK支持自动刷新、错误重试、超时控制能把你从“手动换token”的琐碎里解放出来。以Python为例你可以在SDK的运行时配置里加上凭证提供者的自动刷新机制或者自己封装一个带缓存的客户端类内部定时检查过期时间。这样业务代码永远从客户端类里取凭证完全感知不到凭证曾经变化过。三种方式其实不是互斥的我自己的习惯是用控制台管角色和策略用脚本做临时调试用SDK接入正式业务。它们各管一段组合起来才完整。5. 高频问题与排查经验5.1 Token过期导致请求报错怎么优雅处理临时凭证到期后的报错信息一般是InvalidAccessKeyId或RequestTimeTooSkewed排查起来非常容易检查系统时间和sts服务的时间差值是否过大确认当前时间是否已经过了expiration。解决思路有两个方向。一是把duration_seconds设长一点适合定时任务这种可以预估耗时的场景二是写一个自动刷新逻辑在代码里判断剩余有效期不足5分钟就重新换取。我处理过一个数据同步任务每天要从几十个存储桶拉取文件有一次任务跑了3个小时而token只设了1小时结果中途一片报错。后来我把刷新逻辑抽成公共模块所有任务统一调用再也没出过这类问题。5.2 权限不足AccessDenied的排查顺序遇到AccessDenied时大多数人第一反应是去改角色权限策略但其实应该先做排除法按这个顺序排查先确认扮演是否成功。如果AssumeRole本身报了AccessDenied说明信任策略没放行问题根本不在权限策略上。“拒绝”的判断顺序是先看有没有信任策略允许你Assume再看角色权限策略是否允许目标Action再看会话策略是否进一步限制。再确认目标资源的ARN是否写对。很多人写Resource时把账号ID写错、把路径写错或少了一个通配符都会导致权限不匹配。你可以先用控制台的“策略模拟器”输入一条请求试跑它会明确告诉你具体是哪个策略匹配失败。最后检查是不是被显式拒绝。有些平台有Deny优先规则就算你在Allow策略里加了权限一旦有任何一条策略Deny了同一操作结果仍然是拒绝。检查时特别留意角色上是否绑定了权限边界Permission Boundary这个边界会限制角色权限的最大范围比策略本身优先级更高。5.3 信任策略配好却被拒绝大概率是这三处问题第一个是Principal写错。跨账号授权时Principal里要填账号A的ID和用户名注意不要填成账号B自己也不要写成通配符。我见过几次配置结果是用账号A的ID去扮演账号A自己的角色那当然不会允许。第二个是ExternalId不匹配。信任策略里写了Condition要求ExternalId等于某个值但调用时没传或传错会直接拒绝。记住Trust Policy和AssumeRole请求里必须同时出现且一致。第三个是不小心启用了角色最大会话时长限制。有些平台允许给角色设置最大会话时长如果你创建角色时填了30分钟那调用时duration_seconds超过这个值就会报错而不是自动截断。这个坑很隐蔽因为控制台默认值往往比较大一旦手动改小了很容易踩到。5.4 常见问题速查表现象可能原因排查/解决办法AssumeRole返回Forbidden信任策略Principal未包含调用者检查角色Trust Policy中的Principal和ConditionAssumeRole返回InvalidParameterduration_seconds超范围查询平台支持的上下限调整会话时长拿到Token后调用API报AccessDenied角色无目标Action权限在角色权限策略中补充Action和ResourceAccessDenied但确认角色有权限会话策略收窄了权限检查AssumeRole时传入的policy字段请求返回RequestTimeTooSkewed客户端系统时间和服务器时间偏差过大校准系统时钟NTP同步Token在有效期内但调用还是失败临时AK/SK与SecurityToken不匹配确认三件套是同一接口返回的别混用脚本运行超过有效期报错会话时长小于任务耗时开启自动刷新或调大duration_seconds子用户扮演角色提示无权限子用户缺sts:AssumeRole权限给子用户添加角色扮演的API权限5.5 我在实际项目中踩过的几个坑最后分享几个亲身经历过的教训代价都不小。第一个教训是千万不要把角色权限策略配成一个“超级管理员”。当时有个项目为了图省事给一个跨账号角色直接绑定了全量管理权限结果因为某个下游任务误操作把对方账号里的资源配置改坏了。虽然临时凭证有时效但权限边界没控制好短时间内依然能做很多事。现在我的原则很简单角色的权限能精确到“目录级”就不只写到“存储桶级”能写“只读”就不写“读写”。第二个教训是角色的复用要谨慎。同一个角色被多个用途共用调试时很难分清是哪个任务的会话在调用。后来我给每个业务线单独建角色名称带上业务标识配合role_session_name的命名约定审计日志一眼就能定位。第三个教训是刷新逻辑要放在调用方不要放在权限配置里。你以为调大duration_seconds就能解决所有问题其实只是把风险窗口拉长了。真正稳妥的做法是让调用方感知有效期提前刷新。我用STS的时间越长越觉得它其实就是一句话动态地、受控地把权限借出去。它不复杂但值得你花时间把每个概念吃透因为云上权限的事出问题从来都不是小事。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑