In this post, we’ll dive deep into SAP table authorizations.
Due to the extent of this topic, we need an…
- Standard S_TABU_* authorization checks
- Controlling access by authorization group
- Controlling access by table name
- Cross-client table maintenance
- A deeper look at SE16, SM30 and friends
- Direct program invocation
- Redundant transactions
- Weak parameter transactions
- Other ways to get hold of table data
- Customer programs
- Function modules
- Debug with replace
- Transport requests
1. Standard S_TABU_* authorization checks
Auditors often criticize extensive table authorizations and (shortly afterward) security consultants are asked to check those table rights… So, let’s see how SAP allows us to limit direct table access. “Direct”, as we are dealing with the SAP standard tools for displaying table data on-screen in this article… not to be confused with business transactions that work with (the same) table data, too.
Therefore, the scope of this article is:
- Standard table browsing and maintenance transactions: SE16, SE16N, SE17, SM30, SM31 etc.
- Proxy-transactions like SPRO (which call the aforementioned ones internally)
- SAP Query (SQVI, SQ01, …)
(btw.: the standard table authorizations checked in the above transactions are potentially checked in other tcodes as well, but that’s another topic)
1.1. Controlling access by authorization group
The authorization object S_TABU_DIS controls access to tables via an Activity (ACTVT) in combination with a Table Authorization Group (DICBERCLS ≈ dictionary Berechtigungs-class… nice German-English mix, SAP 😁).
Table Auth. Groups combine an arbitrary number of tables with a similar “purpose” or contents; e.g. the group “SC” (“RS: User control”) includes hundreds of tables containing user master-, address-, audit- and similar data. Each table can only be assigned to a single Table Auth. Group.
You can check a table’s group via SE54 or directly via SE16 (table TTDAT, field CCLASS):
The purpose of a Table Authorization Group (or at least its humble description) can be found in table TBRGT
(go for BROBJ = “S_TABU_DIS”, BRGRU = <group> and check the BEZEI field):
The assignment of tables to Table Auth. Groups can be maintained in SE54 (or directly via SM30 for view V_DDAT_54).
So, with S_TABU_DIS we’re able to grant access “by purpose”… which sounds good and is rather useful… but there are some downsides:
- only a low percentage (usually less than 20%) of all tables are assigned to a Table Auth. Group on a recent IDES system… and the rest? – all other tables automatically belong to the fallback group “&NC&” (= “not classified”).
If you want to check this yourself, just compare the number of entries in table DD02L (with AS4LOCAL = “A”) to the number of entries in TDDAT (with CCLASS ≠ “&NC&” and ≠ “”).
- you always have to grant access to all tables linked to a group, which often feels like taking a sledgehammer to crack a nut. This is a major lack of granularity!
1.2. Controlling access by table name
In recent releases, SAP introduced a new authorization object, which finally offers fine-grained control – S_TABU_NAM (“Table Access via Generic Standard Tools”):
It allows granting access by individual tables names (wildcards are allowed, but only at the end of the TABLE value). S_TABU_NAM and is only considered if a previous check for S_TABU_DIS failed (it’s an additional check after S_TABU_DIS, not a replacement). Thus, it’s now possible to grant display access to a complete Table Auth. Group while limiting maintenance rights to selected tables in that group.
Check out SAP Note 1481950, Note 1500054 and the related ones mentioned therein — strongly recommended.
1.3. Cross-client table maintenance
Client-independent tables are shared among all clients of an SAP system; modifications to their contents have an impact on the whole system. Hence, changes to them require special care and must only be allowed for users who know about the possible side-effects. SAP mitigated this problem by providing a supplementary authorization object, which is only checked when trying to maintain client-independent tables in addition to S_TABU_DIS and _NAM: S_TABU_CLI (“Cross-Client Table Maintenance”). The allowed values for its only field CLIIDMAINT are “X” (allowed) and ” ” (not allowed). Most client-independent tables are customizing tables, so the main audience for this right is administrators and supporters.
At this point, we can summarize the complete flow of authorization checks for table access:
- first S_TABU_DIS is checked using the table’s authorization group (or the dummy group “&NC&” if none is set)
- only if this fails, S_TABU_NAM is checked for the table name
- on success and in case the user wants to modify (not display) a client-independent table, an additional object – S_TABU_CLI – is checked
Update: in release 750, S_TABU_NAM is actually checked first – even though the check logic (“OR”) is not affected by this; see this comment.
2. A deeper look at SE16, SM30 and friends
At this point, we took a look at the basic measures for table protection; it is time for the pitfalls one might want to avoid!
A common mistake, (overworked) authorization admins often make when they’re asked to clean up table authorizations is to just remove SE16 etc. from S_TCODE but leave S_TABU_* as it is.
So, what is the problem with table authorizations without the corresponding tcodes? Simple answer: many roads lead to Rome! – read on…
2.1. Direct program invocation
Transactions are nothing but a convenient way to execute programs, optionally passing parameters along.
Let’s check via SE93, what exactly is behind SE16 and SM30 (check the other ones yourself):
- SE16 calls SAPLSETB, which is a function pool (» function pools are not directly executable)
- SE16N executes the report RK_SE16N (» executable)
- SM30 and SM31 call SAPMSVMA – a module pool (» not directly executable; almost * )
This means, removing SE16N from S_TCODE, but allowing, for instance, SA38 or START_REPORT does not necessarily prevent direct table access.
It’s not a big deal, since table authorizations are checked as usual… but keep in mind: removing the tcodes from S_TCODE has a limited effect unless you also take care of S_TABU_*, S_PROGRAM and S_DEVELOP (check SAP Note 1012066 for the last one)!
(*) Direct module pool execution is possible through a strange specialty in SE38: if a module pool name is entered in the first screen and then executed, SAP internally searches for tcodes using the same program (“SELECT * FROM tstc WHERE pgmna = <NAME>”) and arbitrarily executes the first one! This is done in class CL_WB_PGEDITOR, method EXECUTE.
2.2. Redundant transactions
In the last chapter, we’ve identified the programs behind the most common table transactions. Unfortunately, they’re not the only ones, which point to the underlying programs. A user might hit on the idea to search for a tcode, which executes a program he/she isn’t allowed to execute directly – SE16 or SM30 in our case.
To assess this risk, we have to search for other tcodes (in table TSTC), which use the same programs (in the field PGMNA). Check your system!
2.3. Weak parameter transactions
Parameter transactions execute an existing transaction delivering pre-defined screen input.
For example, the transaction code SM30_PRGN_CUST is a shortcut to SM30 for the maintenance view PRGN_CUST. When you call it, SM30 is executed with “PRGN_CUST” in maintenance mode. Since the option “Skip initial screen” is selected, this tcode jumps directly into the table maintenance view itself – if it wasn’t selected, one would be able to override the given screen options, including the table name! That’s the point where the weakness starts.
Have a look at these parameter transactions from table TSTCP:
The first one – VYCM – is safe, because the “/*” indicates that the first screen is skipped and thus the view name cannot be overridden. The second one – VYCQ – just calls SM30 and fills in the given view name “TKKVBLERM”… leaving the choice of the actual view name up to the user (due to “/N”)!
So, to determine all unsafe parameter transactions for SE16, SM30… you need to search for PARAMs matching “/N<TCD>” (e.g. “/NSM30*”).
3. Other ways to get hold of table data
Now we’re ready to face the truth: there are ways to avoid S_TABU_* checks!
3.1. Customer programs
All customer programs are as secure as the developers made them!
To scare your boss, just show him/her something like this:
REPORT. TABLES: pa0008. " HR Master Record: Infotype 0008 (Basic Pay) SELECT * FROM pa0008 CLIENT SPECIFIED. " SAP Client, User Name, Personnel Number, Annual Salary WRITE: / pa0008-mandt, pa0008-uname, pa0008-pernr, pa0008-ansal. ENDSELECT.
Note: UNAME is actually the user, who last changed the record; to get the SAP user related to the employee, check this comment.
Ways to mitigate this risk include:
- organizational: add a “code check” step to your company’s transport process (i.e. 4 eyes principle)
- internal control: perform a regular control of customer programs (e.g. via report RS_ABAP_SOURCE_SCAN) – but be aware, that this is not safe due to code obfuscation
3.2. Function modules
Many function modules do not perform (sensible) authorization checks, as they are intended to be called from within reports – which are assumed to do those checks instead. If users are allowed to “test” (i.e. execute) FMs, most protection measures become pointless! Concerning table access, this means that in the worst case, the standard checks from the first chapter are just ignored.
Try SE37 with “TRINT_DISPLAY_TABLE_CONTENTS” to view any table you want!
This is what you need:
😯 … ouch – no S_TABU_* check!
Update: meanwhile SAP fixed this and added a proper check to the function module – check Note 1716236.
3.3. Debug with replace
Allowing users to debug with replace (i.e. S_DEVELOP with object type DEBUG and ACTVT 02 & 03) is extremely dangerous, since it allows stepping over parts of the code (e.g. AUTHORITY-CHECKs) and change field values (e.g. SY-SUBRC thereafter)! This means that almost all authorization checks are useless, as they can be bypassed or the return code can be changed. The only checks, which are unavoidable, are kernel-based authorization checks – like S_DATASET.
Example: to display the contents of any table, you only need to set three breakpoints in the includes LSUSEU11, LSETBU03 and LSVIXU16. This can easily be accomplished with the following authorizations (attn.: this is neither the smallest nor the only possible set of authorizations):
Watch out: replacing variables creates a system log message; check SM21!
3.4. Transport requests
Table contents contained in transport requests can be viewed without a single S_TABU_* check ❗
Being authorized to create a new transport request and include a table’s contents is equal to SE16. Btw, there is no need to release the request; it can even be deleted later – without leaving significant traces.
Try this at home (while running an authorization trace):
… and then …
… et voilà.
See also SAP Note 1237762 → Solution → Item 4.
The main takeaways of this post are:
- S_TABU_DIS is suitable for users, who need access to many tables, i.e. admins and most supporters
- explicitly assigning a table to group “&NC&” via SE54 has absolutely no effect – it the same as no entry in TDDAT at all
- S_TABU_NAM is good for users, who only need access to a small number of well-known tables, i.e. some supporters and few business (key-) users
- S_TABU_CLI is strictly for SAP basis admins, especially for (but not limited to) multi-client systems
- table data access is not limited to the standard tcodes SE16(N), SM30 etc. – many other ways exist
- direct report execution is sometimes necessary, but might be evil – be careful
- search your system for alternative tcodes and weak parameter transactions (especially Y… and Z… ones)
- being able to test function modules on production is Russian roulette for your system security!
- don’t let your programmers transport without (ex-ante or ex-post) inspection
- debug with replace on any productive system is evil! really!
- being able to create transport requests on production opens insecurities as big as a barn door (but might be necessary during support package installations)
let’s say you have a role with T-CODE SE16
S_TABU_DIS: ACTVT: 03
S_TABU_NAM: ACTVT: 03
Now log on with a user having the authorizations above.
– Active the trace – STAUTHTRACE
– Go to SE16 and enter the table name: AGR_1016 and execute
– Deactive the trace and evaluate the results
You will see that S_TABU_NAM was checked first.
Of course if S_TABU_NAM was not included in the role,then S_TABU_DIS would be checked successfully.
I think the statement “The system checks S_TABU_NAM only if the authorization check on S_TABU_DIS was unsuccessful” is not correct once S_TABU_NAM is always checked first.
i checked VIEW_AUTHORITY_CHECK in a 750 release and you’re right!
The check for S_TABU_NAM goes first and, if unsuccessful, afterwards S_TABU_DIS is checked.
In the end it is also a logical OR-condition and not much of a difference from an authorization perspective, but I wonder if SAP changed it recently or if it was always like this… because I could swear that it wasn’t…
Kind regards, Daniel
If we check SAP documentation for S_TABU_NAM it’s saying the opposite:
“The object is only checked if the authorization check for object S_TABU_DIS failed”.
Think this should be corrected to avoid any confusion.
I just came across this topic from your blog. Excellently put by the way! But I have one question… suppose there are 2 tables:
1. ABC – (“&NC&” auth. group)
2. XYZ – (“SA” auth. group)
If I have following S_TABU_* objects maintained in my role:
S_TABU_DIS – 02 / SA
S_TABU_NAM – 02 / ABC
Will this allow me to edit table ABC? I have not maintained &NC& explicitly, but I want to know whether will I able to edit table ABC? And if yes, will ST01 show me failed entry for S_TABU_DIS? As in VIEW_AUTHORITY_CHECK, S_TABU_DIS is checked first as per all the information that I have read.
Yes and yes:
When you try to edit table ABC, the system checks S_TABU_DIS first — and since you don’t have _DIS with 02 / &NC& in your example role, this check will fail and result in a failed auth.-check (in SU53 and in a trace).
The next check is for _NAM with 02 / ABC … which is successful – finally you’re allowed to edit table ABC.
Best wishes, Daniel
It’s not correct as I can see in trace file.
The system first check for S_TABU_NAM and if it fails, then a check is made in S_TABU_DIS.
Check VIEW_AUTHORITY_CHECK function module.
This is a really very necessary document. Thanks for sharing…
very informative blog!! It really helped me a lot.
But we have a scenario in our project, where in SAP IT teams need display access for all tables. Since this is a potential risk… is there a way that we can get to know that what tables a user is calling from transaction SE16 or SE16N. A report may be or any program, which we can develop to log this.
Thanks in advance!! 🙂
Hi Prachi, thanks a lot!
Concerning your problem: unfortunately there is no user exit, which would allow logging all table accesses, but the Security Audit Log could help. The message with ID DU-9 (Audit class “Transaction start” → Message “Generic table access call to <TABLE> with activity <ACTVT> …”) could be useful for you, since it logs all table accesses with generic tools (SE16, SE16n, SM30, SQVI, …) including the table name. So you could configure the Security Audit Log (SM19) to log this type of event and evaluate the protocol later.
very nice blog. 🙂
just a few words about the PA0008 as this can lead to confusion.
UNAME is actually the field that old the SAP user who lastly modified the data. If you want to get the SAP user related to the employee, simply perform a SQL to JOIN PA0008 with PA0105 (Communication) for subtype 0001.
In the same idea, you can get the name of the employee from either PA0001-ENAME or PA0002 (Identity).
Your website is a real gem, I love it!
SAP Senior HCM consultant
thanks for this helpful remark!
I added a link to this comment to the post above.
Best regards, Daniel
I just stiblerd across your blog and have been busy reading a lot of the posts and on the whole very good.
You mentioned in this post about SQ01 etc. and that horrid family of query transactions that end-users love.
I wonder your thoughts on these? For example…
A client of mine wishes to give SQ01 to all users. I argued them down to select users, who can demonstrate sensible levels of intelligence, if they want to write a query and that it should be written in QAS first. For the other users I offered them SQ00, as it has no create ability as far as I know and they accepted this.
However I am getting fed up with users asking for access to tables such as T001 and just about every other table from USR02 to MARA to VBAK to 200 other tables in order to run their queries – I don’t remember this being an issue (needing S_TABU_NAM) for SQ01 and assuming (because they said they won’t) the client is unwilling to turn the queries into Z transactions (presumably removing the S_TABU_NAM requirement).
It seems I have to grant SQ00 plus S_TABU_NAM and list every table the query looks at (they just went live and wrote over 200 queries during the implementation) which is becoming a headache so…
Nice eval – it is always the same when companies had rolled out widly the usage of SE16 or SQVI…