Securing Web Services with mod_security
Pages: 1, 2, 3
Gluing a Web Services Resource into mod_security
Blue Bank's web service uses the URL www.bluebank.example.com/axis/getBalance.jws. It's generally a good idea to create a rule set for this resource. To do so, Blue Bank has glued its resource into httpd.conf with:
<IfModule mod_security.c>
SecFilterEngine On
SecFilterDefaultAction "deny,log,status:500"
# Other rules
# ------- Rules for web services --------------------------
<Location /axis/getBalance.jws>
SecFilterInheritance Off
SecFilterDefaultAction "deny,log,status:500"
SecFilterScanPOST On
SecFilterCheckURLEncoding On
SecFilterCheckUnicodeEncoding On
</Location>
#---------------------------------------------------------------
</IfModule>
The following directive block applies filtering criteria for
/axis/getBalance.jws. This adds the required placeholder for the
defense of the web service. Placeholders go in the <Location> block.
# ------- Rules for web services --------------------------
<Location /axis/getBalance.jws>
SecFilterInheritance Off
SecFilterDefaultAction "deny,log,status:500"
SecFilterScanPOST On
SecFilterCheckURLEncoding On
SecFilterCheckUnicodeEncoding On
</Location>
#---------------------------------------------------------------
There are two important directives here:
SecFilterInheritance Off
This directive turns off all other rules and provides a clean space for a new rule set for the enclosing location only.
SecFilterScanPOST On
Web services invocation methods go over POST. Hence, the next directive to
enable is SecFilterScanPost to enable POST filtering.
With the above details in place, Blue Bank has deployed a shield in the form
of mod_security. It also knows its defense target--the content of id, which
clients pass to the web service over a SOAP envelope.
Defending Attack Vectors
As a first step to defend all incoming malicious requests, Blue Bank needs
to trap the value of id to prevent a client from passing an invalid value.
The SOAP request uses an XML tag to pass this id information to the internal
web services code, looking something like:
<q1:getInput xmlns:q1="http://DefaultNamespace">
<id xsi:type="xsd:string">12123</id>
</q1:getInput>
To filter the request, mod_security must have some way to read the value
associated with the tag; in this case it is 12123. mod_security provides ways
to trap a value passed through a POST request. One technique is to use
a customized filter:
<Location /axis/getBalance.jws>
SecFilterInheritance Off
SecFilterDefaultAction "deny,log,status:500"
SecFilterScanPOST On
SecFilterCheckURLEncoding On
SecFilterCheckUnicodeEncoding On
SecFilterSelective POST_PAYLOAD "<\s*id[^>]*>" chain
</Location>
The highlighted line traps the request made to id. POST_PAYLOAD
intercepts the POST data block and tries to match the regular
expression pattern (<\s*id[^>]*>). This regex
pattern ensures that the tag for id exists. Only then does it proceed with
the rest of the checks (because of the chain directive). In other
words, if the tag id exists, mod_security continues with the next
check.
If the sent POST request has an id contained in it, the server can process
the information. However, a malicious client can modify this particular value
to inject malicious content into it. There are four main popular attack
vectors.
Attack vector 1: variable-length buffer injection
One of the major security concerns when passing a large buffer to a variable
is that a large buffer may cause the application to misbehave and/or crash somewhere down the line during execution. The following rule defends the variable
id against precisely this type of attack:
<Location /axis/getBalance.jws>
SecFilterInheritance Off
SecFilterDefaultAction "deny,log,status:500"
SecFilterScanPOST On
SecFilterCheckURLEncoding On
SecFilterCheckUnicodeEncoding On
SecFilterSelective POST_PAYLOAD "<\s*id[^>]*>" chain
SecFilterSelective POST_PAYLOAD "<\s*id[^>]*>.{6,}</\s*id\s*>"
"deny,status:500"
</Location>
In the above directive, the regular expression pattern
<\s*id[^>]*>.{6,}</\s*id\s*> restricts the buffer to
allow only a buffer of five characters. In order to ascertain the effect of the
above directive block, Blue Bank can send across two requests, one that matches
the buffer length set and the other that exceeds the buffer length set in the
directive block.
POST /axis/getBalance.jws HTTP/1.0
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Content-Length: 576
Expect: 100-continue
Host: www.bluebank.example.com
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="http://www.bluebank.example.com/axis/getBalance.jws" xmlns:types="
http://www.bluebank.example.com/axis/getBalance.jws/encodedTypes"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<q1:getInput xmlns:q1="http://DefaultNamespace">
<id xsi:type="xsd:string">12123</id>
</q1:getInput>
</soap:Body>
</soap:Envelope>
...
HTTP/1.1 200 OK
Date: Mon, 03 Jan 2005 19:24:10 GMT
Server: Apache/2.0.50 (Unix) mod_ssl/2.0.50 OpenSSL/0.9.7d mod_jk2/2.0.4
Set-Cookie: JSESSIONID=69C6540CC427A8B064C0795ADDFC20EA; Path=/axis
Content-Type: text/xml;charset=utf-8
Connection: close
<?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<soapenv:Body>
<ns1:getInputResponse
soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:ns1="http://DefaultNamespace">
<ns1:getInputReturn
xsi:type="xsd:string">$2500</ns1:getInputReturn>
</ns1:getInputResponse>
</soapenv:Body>
</soapenv:Envelope>
In the above case, a buffer of five characters is passed, and the server sent a
response with the value of $2500. Here's the response and request with a
modified id of 121234 (six characters):
POST /axis/getBlalance.jws HTTP/1.0
Content-Type: text/xml; charset=utf-8
SOAPAction: ""
Content-Length: 577
Expect: 100-continue
Host: www.bluebank.example.com
<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:soapenc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:tns="http://www.bluebank.example.com/axis/getBalance.jws" xmlns:types="
http://www.bluebank.example.com/axis/getBalance.jws/encodedTypes"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body
soap:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<q1:getInput xmlns:q1="http://DefaultNamespace">
<id xsi:type="xsd:string">121234</id>
</q1:getInput>
</soap:Body>
</soap:Envelope>
...
HTTP/1.1 500 Internal Server Error
Date: Mon, 03 Jan 2005 22:00:33 GMT
Server: Apache/2.0.50 (Unix) mod_ssl/2.0.50 OpenSSL/0.9.7d mod_jk2/2.0.4
Content-Length: 657
Connection: close
Content-Type: text/html; charset=iso-8859-1
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN">
<html><head>
<title>500 Internal Server Error</title>
</head><body>
<h1>Internal Server Error</h1>
<p>The server encountered an internal error or misconfiguration and was
unable to complete your request.</p>
<p>Please contact the server administrator, you@example.com and inform
them of the time the error occurred, and anything you might have done that
may have caused the error.</p>
<p>More information about this error may be available in the server
error
log.</p>
<hr />
<address>Apache/2.0.50 (Unix) mod_ssl/2.0.50 OpenSSL/0.9.7d
mod_jk2/2.0.4
Server at 192.168.7.50 Port 80</address>
</body></html>
The mod_security module has rejected this request. Status 500 is the response received. This indicates that the request never hit the web services level. Blue Bank has succeeded, therefore, in providing a sound defense against any kind of buffer overflow, the most common and often ignored vulnerability.
