做SAP权限和审计这几年,我最常看到的情况不是权限不够,而是权限大得离谱。打开一个角色,授权页签里密密麻麻的通配符,S_DEVELOP是*,S_TABU_DIS是*,S_RFC是*,S_TRANSPRT也是*,说句不夸张的话,这种角色如果挂在生产系统里,再叠加一个Dialog用户类型,基本等于给持有者发了一张“系统管理员体验卡”,而且不用走任何审批流程。我见过不止一个项目在季度安全盘点时,才发现外部顾问的角色里安安静静躺着这类高危授权,时间跨度以年为单位。
这篇文章围绕SAP ABAP特殊授权对象的保护措施展开,适合BASIS、权限安全顾问、ABAP开发负责人,以及所有需要审核角色设计的人参考。我会把高危授权到底高在哪、怎么把系统里握着这些授权的用户全部挖出来、做角色收敛时哪些字段该怎么改、上线之后怎么防止它再长回去,这几个问题完整过一遍。
1. 先把风险点定位清楚:危险的不是事务代码,是授权对象
1.1 ABAP权限检查的基本链路
很多人一说高危权限就只想到两个东西:一个是SU01里给用户挂了SAP_ALL,另一个是SE38、SE80这类事务代码。实际上在ABAP权限模型里,事务代码本身几乎不参与真正的权限判断。它只是一个入口、一个菜单项,程序运行到关键节点时,靠代码里一行一行的AUTHORITY-CHECK语句来检查授权对象,授权对象里的字段值才是真正的锁。
比如一个用户能打开SE38,但没有S_DEVELOP的变更权限,他只能干看着源码,改不了也保存不了。反过来讲,如果角色里的S_DEVELOP字段值全是*,哪怕你不给他SE38这个事务代码,他也能找到其他路径进入编辑器,甚至在自己的自定义报表里触发程序修改。因此,判断一个权限高危不高危,别只看事务代码名单,要下沉到角色里授权对象的字段值。
1.2 高危授权对象清单:每一个都值得单独列进审计规则
下面列几个我每次做权限盘点必定会查的授权对象,它们在大多数ABAP系统里属于“给了就等于半个管理员”的存在。
| 授权对象 | 关键字段 | 拿到危险值意味着什么 | 常见被误分配场景 |
|---|---|---|---|
| S_DEVELOP | OBJTYPE / OBJNAME / P_GROUP / ACTVT / DEBUG | 新建、修改、删除ABAP程序、类、函数组等开发对象;DEBUG权限还能在调试器里改内存变量 | 给所有开发顾问配同一个“全量开发角色” |
| S_TABU_DIS | DICBERCLS / ACTVT | 通过SM30等方式直接表维护,绕过应用逻辑改任何表的数据 | 为了让运维省事,直接放行表维护 |
| S_RFC | RFC_TYPE / RFC_NAME / ACTVT | 以该用户身份远程执行系统内任意函数模块 | 作为“接口账号”一键给了通用RFC权限 |
| S_PROGRAM | PROGRAM / P_GROUP / P_ACTION | 运行或提交执行任意ABAP程序 | 开发角色顺手勾了“执行所有程序” |
| S_TRANSPRT | TR_EVENT / ACTVT | 发布传输请求,把开发对象搬运到测试甚至生产 | 给开发角色默认挂了传输管理权限 |
| S_ADMI_FCD | S_ADMI_FCD | 持有SP01、SPAD等系统管理功能代码,可操作系统级配置 | 为省事给一个*了事 |
| S_BTCH_ADM | JOBGROUP / ACTVT | 管理所有后台作业,停止、改参数、删除作业 | 运维角色图省事直接给全 |
| S_USER_AGR / S_USER_GRO | AGR_NAME / ACTVT | 维护角色、用户组,等于能给自己发权限 | 权限管理员以外的人误挂 |
逐个说危险的细节。
S_DEVELOP的字段组合很关键:OBJTYPE限定能操作什么类型的开发对象,OBJNAME限定具体对象名,P_GROUP限定程序组,ACTVT决定增删改查,DEBUG字段则控制能不能进ABAP调试器。调试器这玩意危险在哪?一旦进入调试态,你可以临时修改变量值、跳转逻辑、调用任意功能模块,等于在系统里拥有了临时的“任意代码执行”能力。很多开发团队对S_DEVELOP的ACTVT和DEBUG管控不严,这是最大的隐患之一。
S_TABU_DIS是表维护权。SM30/SM31这类事务底层检查的就是它。DICBERCLS是数据字典类别,ACTVT是活动,常见值02改、03显示。按SAP标准机制,这个授权对象并不支持“只允许维护MARA这一张表”的细粒度配置,一旦DICBERCLS给了*,就意味着用户可以进入表维护生成器,直接改任意一张表的数据。BSEG、MARC、MSEG这类底层数据表在里面,财务总账、库存成本全裸奔。
S_RFC是远程函数调用权。字段RFC_TYPE通常对应功能组,RFC_NAME是功能模块或功能组名,ACTVT=16表示执行。如果RFC_NAME给了*,等于允许以这个用户的身份调用系统任何RFC函数模块。尤其当这个权限挂在服务账号上,又恰好在SM59配置了信任关系,那就不是单系统风险了,它可以横向摸到其他信任系统。
S_PROGRAM是程序级权限,重点看PROGRAM和P_ACTION。PROGRAM=*且P_ACTION=SUBMIT时,用户能运行任意ABAP程序。很多人觉得“跑个报表有什么关系”,但标准报表里有不少带后遗症的,比如后台执行、批量修改、发消息、写表。给外部顾问这个权限,他还真能绕过事务代码层去触达大量功能。
1.3 真正致命的是组合:从“改个程序”到“绕过逻辑改表”
单个对象看,每个都像是一个侧面,但用户权限是叠加的,风险要看组合之后的爆炸半径。我实际排查中见过最危险的三种组合。
第一种,S_DEVELOP全值+S_TRANSPRT全值。开发者在系统里改完代码,自己新建一个传输请求,直接释放,一路搬到测试甚至生产。两个权限一叠加,变更审批流程就成了摆设。
第二种,S_TABU_DIS全值+任意前台事务。有了它不需要懂任何业务逻辑,直接去改BSEG、MARC、MSEG这些底层表,数据库层面绕过所有应用校验。财务月结对不上账、库存成本异常,倒查起来十有八九能撞上这种组合。
第三种,S_RFC全值+S_ADMI_FCD全值。等于给系统留了一个可远程执行管理动作的后门,尤其在账号同时是服务账号的情况下,操作日志很难落到具体人头上。
所以后面盘点时,我从来不只是查“谁有S_DEVELOP”,而是查“谁同时有哪几个高危对象”,看的是组合结果。
2. 盘点:把系统里握着高危授权的人全部揪出来
2.1 SUIM反查:从授权对象出发,比从用户出发效率高得多
盘点第一步不要从用户逐步翻。用户量大、角色嵌套多,逐个点开看根本不现实。正确做法是走事务码SUIM进入用户信息系统,在Users节点下选按授权对象查询(By Authorization Object或按授权对象值查询),输入S_DEVELOP、S_TABU_DIS、S_RFC、S_TRANSPRT等,执行后系统会把拥有这些对象的复合角色、单角色、直接授权用户全部列出来。
SUIM的好处是它会处理角色继承关系。一个用户通过复合角色继承了某个单角色,SUIM依然能回溯到用户。这个在手工翻主数据时非常容易漏。查询结果记得用Excel导出,作为后面清理工作的基线。导出时不要只留用户名,把角色名、授权对象、字段值一起拉出来,方便做字段级分析。
2.2 RSUSR002批量扫描:给高危授权做“体检报告”
如果系统权限复杂度比较高,或者你已经有一批“重点怀疑对象”,直接用报表。在SE38里运行RSUSR002,选择按授权对象查询,把S_DEVELOP、S_TABU_DIS、S_ADMI_FCD、S_RFC、S_TRANSPRT、S_PROGRAM、S_BTCH_ADM这些对象都输进去,跑完会生成一份持有这些授权对象的角色和用户清单。
这个报表很适合用来做“高危授权月报”或“季度体检报告”。把它固定在周期性任务里,每次扫完发给各模块负责人确认,比临时出事再翻权限高效得多。另外还有个报表RSUSR100,可以用来查用户主数据里的关键状态,比如默认密码、长期未修改密码、锁定状态等,建议配合使用。
需要注意,RSUSR002的标准输出更多是“谁有这些对象”的清单,不一定把每个字段值完整展开。真要判断通配符风险、DEBUG开关是否打开,还得回PFCG或SUIM里看授权数据,不能只靠报表下结论。
2.3 用底表兜底核对:USR02、AGR_USERS、AGR_1252一条线查到底
报表有时候不够用,或者你想在后续做自动化巡检,就得直接摸到底表。下面这几张表是我常用的:
- USR02:用户主数据,重点看USTYP字段,区分Dialog、System、Service等用户类型。
- AGR_USERS:角色和用户的对应关系表。
- AGR_1251 / AGR_1252:角色授权对象定义和授权字段值表。
- USR04 / USR12 / USRPROF:用户直接收到的授权配置文件和用户级授权值。
在SE16N里打开AGR_1252,输入OBJECT='S_DEVELOP',能看到所有把S_DEVELOP放进角色的条目;拿到AGR_NAME之后,再到AGR_USERS里反查有哪些用户挂了这些角色;最后去USR02看用户类型和锁定状态。这条线走一遍,高危授权的实际持有人就基本清楚。
这里有个容易漏的点:有些管理员图省事,直接在SU01用户主数据里挂授权配置文件,或者直接改USR12里的授权值,这种“主数据直接授权”不会体现在角色设计层面。所以兜底核对一定要看USR04、USR12,尤其是那些历史遗留账号,经常能翻出这种手工作业留下的权限。
3. 把特殊授权对象关进笼子里:我守住的四条硬规矩
3.1 通配符清零:所有*都要被审问一遍
授权字段里的是安全最大的敌人。设计角色时,凡是出现的字段,我都习惯问三个问题:这个字段是干嘛的?当前角色真的需要全部值吗?能不能改成Z*或具体值?
S_DEVELOP的OBJNAME从改成Z,开发顾问就只能动自己开发的程序,碰不了SAP标准程序,也碰不了别人的程序。S_DEVELOP的ACTVT也要收敛:普通开发给01、02、03就够,尽量不给06删除;对已经上线的系统,S_DEVELOP的变更权限能收就收。S_PROGRAM的PROGRAM从改成Z,就不能随手运行任意程序了。S_RFC的RFC_NAME从改成Z,整体暴露面就小了一大截。
通配符清零并不是机械地把所有都改成Z,而是先把逐项列出来,然后决定每一处是放行、收紧还是直接删。真正要防止的,是那种授权页签一展开全是、连维护的人都说不清用处的情况。
3.2 角色按职责拆开:开发、运维、业务三把钥匙
我看到过最典型的问题就是“身兼数职”。一个顾问既做开发又管运维,角色里既挂S_DEVELOP又挂S_TABU_DIS。ABAP权限模型本身没办法按“当前系统是生产还是测试”自动限权,授权对象里根本没有这个维度。咱们只能用角色设计来补。
生产环境里绝不分配带S_DEVELOP变更类或S_TRANSPRT发布权限的角色。传输发布权独立成一个角色,只给传输协调负责人。开发和测试环境的“高权限开发角色”与生产环境的“运维角色”命名要严格区分,比如ZDEV_前缀只给开发测试用,ZOPS_前缀只给生产运维用,生产用户主数据里只挂ZOPS开头的角色。业务人员默认不碰开发类角色。
一个简化版的授权矩阵供参考:
| 职责 | S_DEVELOP | S_TABU_DIS | S_RFC | S_TRANSPRT |
|---|---|---|---|---|
| 开发顾问 | 开发/测试给Z*变更;生产最多给显示 | 默认不给 | 按白名单 | 不挂 |
| 生产运维 | 不给变更,最多显示 | 默认不给,紧急走独立流程 | 按白名单 | 独立角色统一管控 |
| 业务关键用户 | 不给 | 不给 | 不给 | 不给 |
3.3 高危对象默认禁用,白名单放行
默认情况下,看到S_TABU_DIS为*、DEBUG勾选、S_ADMI_FCD为*、S_USER_AGR挂着02这类情况,第一反应不要是“这个权限应该给谁”,而应该是“这个权限凭什么存在”。
DEBUG权限默认关闭。需要调试时走临时授权,调完就收,尽量不要长期挂在一个正式角色里。S_TABU_DIS默认不分配,如果业务真的需要直接维护自定义表,建议自己写一个带自定义授权对象的表维护报表,在程序里校验表名和操作类型,然后调用标准表维护函数完成更新。这样就能把“只能维护某几类表”落到代码层面,比裸开S_TABU_DIS可控得多。S_ADMI_FCD只给业务真正需要的那一个功能代码,比如打印重发只给SP01,别顺手给整个*。S_USER_AGR、S_USER_GRO这类用户管理授权,在任何普通角色里都不应该出现,只能由权限管理员放在独立角色里。
3.4 组合即边界:给每个高权限角色画“爆炸半径”
每次角色会审,我要求自己把这个角色的所有授权对象拼起来看。一个开发顾问如果在生产环境同时有S_DEVELOP显示权限、S_TABU_DIS显示权限、S_BTCH_ADM管理权限,虽然单个看都不算“变更权限”,但组合起来,他既能看全源码,又能看底层表,还能操作所有人的后台作业。信息面已经失控了,将来一句话都可能成为安全事故的源头。
按“最坏情况”画边界,再决定是不是要收敛。这条规矩后面做审计季报时也成立。
4. 实战:把ZDEV_ALL从万能钥匙收敛到够用且放心
4.1 先摸现状
我拿一个最典型的开发顾问角色ZDEV_ALL举例。它在PFCG授权页签里的现状大致是这样:
- S_DEVELOP:OBJTYPE=,OBJNAME=,P_GROUP=,ACTVT=,DEBUG勾选
- S_TABU_DIS:DICBERCLS=,ACTVT=
- S_RFC:RFC_NAME=*,ACTVT=16
- S_PROGRAM:PROGRAM=*,P_ACTION=SUBMIT
- S_TRANSPRT:ACTVT=*
- S_ADMI_FCD:S_ADMI_FCD=*
- S_BTCH_ADM:JOBGROUP=,ACTVT=
这种角色看着万能,实际工作中真正高频用到的功能很有限。动手改之前,我先做三件事:用SUIM把该角色当前授权对象全量导出;用SU53和权限审计日志看最近一个季度这些开发人员实际触发过哪些权限检查失败;和模块负责人确认这群人日常到底干什么。
开发顾问日常也就是写Z开头程序、跑Z报表、处理自定义表数据,偶尔调试一下。他不需要改生产标准表,不需要发布传输请求,更不需要管理所有人的后台作业。
4.2 收敛动作明细
| 授权对象 | 收敛前 | 收敛后 | 收敛理由 |
|---|---|---|---|
| S_DEVELOP | 全*,DEBUG勾选 | OBJTYPE限定PROG/FUGR/CLAS等对象类型,OBJNAME=Z*,ACTVT=01/02/03,DEBUG不勾 | 能开发Z对象,不能碰标准程序,不开调试后门 |
| S_TABU_DIS | DICBERCLS=,ACTVT= | 移除 | 需要维护自定义表数据走自开发事务,不开放通用表维护 |
| S_RFC | RFC_NAME=*,ACTVT=16 | RFC_NAME=Z*,按功能组白名单 | 只能调用团队自己的函数组,不能摸遍系统内所有RFC |
| S_PROGRAM | PROGRAM=*,P_ACTION=SUBMIT | PROGRAM=Z*,P_ACTION=SUBMIT | 只能运行Z开头报表,不能随手SA38跑标准程序 |
| S_TRANSPRT | ACTVT=* | 从角色中移除,发布权限独立 | 开发和发布拆开,杜绝自己改完自己上线的链路 |
| S_ADMI_FCD | S_ADMI_FCD=* | 清空,按需临时申请 | 没有固定理由不给系统管理功能 |
| S_BTCH_ADM | JOBGROUP=,ACTVT= | JOBGROUP=Z*,按需给ACTVT | 只能管理自己组的作业,不能动财务月结等关键任务 |
修改方式就是在PFCG里打开角色,进入授权页签,点击“维护授权数据”,把各对象的字段值逐项改掉。改完保存之后一定要点“生成”,让系统重新生成授权配置文件。这一步特别容易被漏掉,文件没生成,用户那边实际权限不会变化,等于白改。
这里有个ABAP权限检查的小知识:同一个授权对象里的多个字段之间是AND关系,同一个字段如果配了多个值则是OR关系。也就是说,如果团队确实需要维护少数几个SAP标准程序,比如做用户出口增强,可以在S_DEVELOP授权数据里再增加一行,OBJTYPE填PROG,OBJNAME填具体程序名,而不是把OBJNAME整体改回*。多个白名单值共存,不影响安全性。
4.3 验证与回滚:别把干活的人改到骂娘
收敛完必须验证,否则会变成“我什么都干不了了”的现场投诉。我习惯这样操作:
先在PFCG里用比较授权数据功能,对比旧角色和新角色的授权差异,确认移除的东西和我们计划一致。然后把新角色分配给一个测试用户。这里注意,权限变更要用户重新登录才生效,因为授权配置文件在登录时加载。用户没重新登录就来问“为什么权限没变”,是这个阶段最常遇到的误会。
测试用户重新登录后,实际跑一遍核心事务:SE80打开已有Z程序,SE24改Z类,SE38跑Z报表,调用一个日常用的BAPI程序。看到权限检查失败时,不要急着把*加回去,用SU53看具体卡在哪个授权对象、哪个字段值。能按白名单放行的就加白名单,确实业务场景需要但不在收敛范围内的,再回到旧角色评估。
批量切换时,旧角色ZDEV_ALL保留一个冻结副本,比如改名ZDEV_ALL_OLD,从业务用户身上移除,但不要在系统里直接删角色。观察两到四周,没有人提交补权申请,再归档处理。这样既不影响业务,又给自己留了后悔药。
5. 长期保护:紧急授权、审计节奏与日志留痕
5.1 临时授权必须自动过期
SAP标准权限模型里没有“有效期”这个概念,角色导入了就一直在。但是权限审批流程里只要有“临时”“紧急”“短期支持”这些动作,就必须考虑自动回收机制。
我的做法是这样的:
- 紧急权限只允许放在独立临时角色里,命名约定Z_TMP_或Z_URG_开头,严禁改正式角色。
- 角色描述里写清申请人、授权人、用途、过期日期。
- 没有GRC这类授权治理平台时,写一个小后台作业定期扫描这些临时角色,超过过期日期就把角色从所有用户中移除,并发邮件通知权限管理员。
用ABAP实现一个最简版本,思路大概是先建一张自定义表ZROLE_EXPIRY记录角色过期日,然后扫描所有以Z_TMP_开头的角色,把已经过期的用户角色关联删掉:
DATA: lt_users TYPE TABLE OF agr_users, ls_agr TYPE bapi_agr_users. SELECT * FROM agr_users INTO TABLE lt_users WHERE agr_name LIKE 'Z_TMP_%' AND NOT EXISTS ( SELECT 1 FROM zrole_expiry WHERE role = agr_users~agr_name AND exp_date > sy-datum ). LOOP AT lt_users INTO DATA(ls_user). CLEAR ls_agr. ls_agr-agr_name = ls_user-agr_name. CALL FUNCTION 'BAPI_USER_ACTGROUPS_DELETE' EXPORTING username = ls_user-uname TABLES actgroups = lt_actgroups. COMMIT WORK. ENDLOOP.代码结构可以根据企业角色命名规范调整,核心逻辑就是把临时角色变成一种“会过期的东西”。如果公司已经有GRC或IDM平台,优先用平台自带的临时权限管理,效果更完整。没有平台,这个ABAP作业就是兜底方案。
临时授权到期不回收,是审计时最常发现的问题。很多所谓“临时”最后都变成了长期后门,这一点比一开始不给权限更值得警惕。
5.2 审计节奏:季度扫描、半年度复核、年度SoD
权限治理不能靠一次集中活动解决,得定节奏。
季度例行:跑一次RSUSR002,扫描高危授权对象清单,导出Excel后发给各模块负责人确认是否仍然需要,确认结果归档。这个动作同时也在积累后续清洗授权的依据。
半年度全面:用SUIM检查每个Dialog用户的角色数量、用户类型;核对USR02里长期未登录用户;检查服务账号的密码策略和登录限制。重点看那些挂了三个以上高权限角色的账号,这是SoD冲突的高发区。
年度SoD矩阵:制定一份“不能同时拥有”的权限组合表,比如能改程序和能发布传输不可并存,能维护角色和能审批角色不可并存。然后对照用户实际角色做交集。没有GRC工具的话,最基础的方法就是报表导出后用Excel做交叉比对。
| 审计类型 | 主要工具 | 核心关注点 |
|---|---|---|
| 季度例行 | RSUSR002 / SUIM | 高危授权对象持有人是否变化 |
| 半年度全面 | SUIM / USR02 / AGR_USERS | 僵尸账号、用户类型异常、角色堆积 |
| 年度SoD | PFCG角色对比 / Excel矩阵 | 互斥权限组合是否出现交集 |
5.3 给高危账号开“摄像头”
定期盘点解决的是“过去和现在”,实时留痕解决的是“当下和追溯”。SM19可以配置安全审计日志,按用户、按授权对象、按事务来记录访问行为;SM20用来查看和分析审计日志。
我建议对高风险授权对象,包括S_TABU_DIS、S_DEVELOP变更类、S_USER_AGR等,以及对持有这些权限的用户,开启审计记录。审计日志定期导出归档,至少要保留一个完整财年。一旦出现生产数据异常或者权限争议,先翻审计日志,比到处问人快得多。
我在实际项目里见过把SAP_ALL直接甩给外部顾问的,也见过S_DEVELOP安安稳稳躺在长期角色里三五年没被清理的。把高危授权关进笼子,技术上并不复杂,难的是坚持。季度扫一次,权限申请必须过审批,临时权限一定要过期,这套动作做扎实之后,系统反而更省心——开发写不了生产、运维改不了业务表、谁做过什么都查得着,半夜被拉起来救火的次数会明显少很多。