Daniel Berlin on Security Insight on SAP security, development stuff… and all the rest

5Feb 12

SAP Web AS protection with an application firewall

Hello there,
this article is intended to demonstrate the potential of securing SAP webservices on the application level.
Concretely, we'll protect the SAP GUI for HTML (aka. WebGUI) from malicious login attempts via the internet using a web application firewall.

The challenge

Let's assume that we want to make a SAP webservice available for direct access from the internet – the SAP WebGUI in this example.

Of course, we are not interested in bad guys trying to break into our system; but usually they don't care about what we want.
The easiest way of getting access to a SAP system without knowing anything about it, is to try the standard users with a list of (well-known) passwords.

The users of choice for this are SAP*, DDIC and friends, so protecting them inside the system is a good idea – but no matter, how secure their passwords are, break-in attempts are always dangerous and should be prevented at the root!
Therefore, the protection method discussed in this article blocks all login attempts for the two users above before they reach the SAP system.

So our goal is to reject all logon requests for DDIC and SAP* from the internet.

Background — What's a web application firewall (WAF) ?

A WAF is a protection measure on the application layer, which applies a customizable set of rules to HTTP conversations and reacts to matches. It is usually operated behind a standard firewall (packet filter) as an additional security layer.

Below, we will use ModSecurity as our WAF, as it's a mature and flexible solution used in many installations around the world. It operates as a module inside the Apache web server and is able to inspect and process input from clients before passing it to the destination (i.e. a SAP webservice). It's even capable of intercepting the output before its transmitted back to clients (this way you could close information leaks due to buggy server-side applications). Btw.: it works for encrypted traffic too, because it has access to transaction data before SSL encryption and after SSL decryption. With these features, ModSecurity can mitigate many threats – if you supply it with appropriate rules.

This is what you need:

In the final setup, inbound requests to the SAP system will pass through two firewall instances (unless you go without a packet filter *cough*):

Setup (1) — Preparation

Before we can proceed to the interesting part, the required framework needs to be prepared.

Steps:

  1. Set up an Apache HTTP server
  2. Secure it
  3. Install ModSecurity – as a precompiled package or by compiling it yourself

The first step is out of the scope of this article, but there's plenty of information on the internet (start here).
Since Apache serves as the "runtime environment" for ModSecurity, it's strategic to protect it. You can find tips this way.
And finally, if you are seeking assistance for the ModSecurity installation: the official reference manual is a good starting point.

Setup (2) — Reverse proxy configuration

Now, we're ready to direct all requests from the internet to the SAP Web AS through our Apache (» reverse proxying).

Steps:

  1. Configure mod_proxy to point to your SAP server's ICM
  2. Adjust your firewall NAT rules to point to the Apache instead of the SAP server directly
  3. Test the new setup

A simple example of a working mod_proxy configuration looks like this:

ProxyPass        / http://sap-host:8000/
ProxyPassReverse / http://sap-host:8000/

Background — ICM logon procedures

Before we continue to the WAF setup, we have to look at the methods available for user authentication in SAP ICM, as this determines what aspect of the HTTP communication we have to examine.

The ICM offers several authentication methods, which SAP calls "Logon Procedures". The one used for a specific webservice can be customized in the "Logon Data" tab in SICF.
We'll deal with the two most basic ones – "Fields Authentication" and "Basic Authentication" – since they allow us to extract the username very easily.

Setup (3) — Web application firewall

Finally, we're ready for the interesting part!
First, ModSecurity needs to be provided with a reasonable base configuration (» official recommendation); after that, we can create our own rules using the "SecRule" directive.
Each SecRule has three parameters, the variable to inspect, the operator to use for the inspection and one or more actions. Long lines may be wrapped with a backslash.

Depending on the logon procedure, one of the following sets of rules is suited for the job…

Rules for the "Fields Authentication" logon procedure:

SecRule  REQUEST_FILENAME   "^/sap/bc/gui/sap/its/webgui" \
                            "phase:2,t:normalizePath,chain,deny,log,logdata:'%{TX.1}'"
 SecRule ARGS_POST:sap-user "^(ddic|sap\*)$" "t:trim,t:lowercase,capture"

Explanation:
The first SecRule line inspects the REQUEST_FILENAME variable, which contains the relative request URL; in our case it is matched against the path of the "SAP GUI for HTML" webservice. The second line contains the associated actions and consists of several parts:

  • "phase:2" means that the rule is processed in the second communication phase (so the request body is available)
  • "t:normalizePath" is a function, which is applied to the variable before it is matched (» description)
  • "chain" means that this rule is chained to the next one – i.e. if it matches, the next rule is evaluated
  • "deny" makes ModSecurity intercept the request, if this rule (and the rest of the chain) matches
  • "log" activates logging and "logdata:'%{TX.1}'" adds more detail to the log entry (» explanation)

The second SecRule line tests the POST parameter "sap-user" against the regular expression specified in the operator and thus matches either "ddic" or "sap*".
The actions at the end have the following meaning:

  • "t:trim" removes leading and trailing whitespace from the "sap-user" parameter
  • "t:lowercase" converts the username to lowercase
  • "capture" saves the matched string (either "ddic" or "sap*") to the variable "TX.1"… which is then logged

If the second rule also matches, the final action from the first SecRule is executed: the connection is denied and a log entry is generated.

Rules for the "Basic Authentication" method:

SecRule    REQUEST_FILENAME              "^/sap/bc/gui/sap/its/webgui" \
                                         "phase:1,t:normalizePath,chain,deny,log,logdata:'%{TX.1}'"
 SecRule   REQUEST_HEADERS:Authorization "^Basic +([a-zA-Z0-9]+=*)$" "t:trim,capture,chain"
  SecRule  TX:1                          "^([^:]+):"                 "t:base64Decode,capture,chain"
   SecRule TX:1                          "^(ddic|sap\*)$"            "t:trim,t:lowercase"

The first SecRule is almost identical to the one used for "Fields Authentication" above. The only difference is the phase, the rule is executed in: "phase:1" means that the rule is processed in the first communication phase when the request headers are available (but the body isn't yet). This is sufficient, since the whole rule chain works with header data only – no need to wait for the body.

After the first rule matched (i.e. the request URL points to the WebGUI service), the second rule is processed. It inspects the "Authorization" HTTP header and matches it's value against a regular expression (» background information). The actual login data (which is a base64-encoded string containing the username and password) is braced and thus saved to the variable "TX.1" by the "capture" action. The "t:trim" function trims the header value before processing (just to be sure) and the "chain" action makes ModSecurity evaluate the next rule.
At this point, the request URL matched and the "Authentication" header's value has been saved in the variable "TX.1".

Rule #3 does not do any filtering, it just base64-decodes the authentication data in variable "TX.1" (via the "t:base64Decode" function), captures the result in the same variable and chains the next rule.

The last rule finally checks the extracted username against the list of forbidden ones. If it matches, the current request is denied immediately.

Mission accomplished 😎

Next steps

You might want to:

  • protect further standard users, such as SAPCPIC, TMSADM and friends
  • alternatively, you could adjust the rules to filter all users except for those in a specific namespace
  • create more rules to prevent bad things from happening to your other webservices
  • check the ModSecurity Core Rule Set, which provides a wealth of generic and useful rules