Is your web application secure? HTTP attacks are real, and dangerous
According to a recent research paper by the Aspect Security Group, entitled The Unfortunate Reality of Insecure Libraries, “Eighty percent of the code in today’s applications comes from libraries and frameworks, but the risk of vulnerabilities in these components is widely ignored and under appreciated. A vulnerable library can allow an attacker to exploit the full privilege of the application, including accessing any data, executing transactions, stealing files, and communicating with the Internet. Organizations literally trust their business to the libraries they use.”
When validating user input from forms and exposed services, we often ignore the URL or think to ourselves, “that information is validated later, it’ll be fine,” but when hacks like the following start turning up – in common web-frameworks – it’s time to start thinking seriously about URL validation. Fortunately, it’s easy to accomplish using a number of methods, but first, let’s look at how these attacks work.
If you look at the attacks sited as examples in the Aspect Security report, you’ll see that both use the URL as the attack vector, or point of access, and both use the execution of
The next example, surprisingly enough, is applications that use the Spring Expression Language (EL). I wouldn’t be too shocked if this problem actually extends to most EL implementations at the time when this exploit was discovered. After doing a bit of digging, JBoss Seam suffered from a similar vulnerability, and has subsequently been updated to close the gap; I’m sure Spring has done the same, but there are still compromised versions of these frameworks running on live servers across the world.
The last vulnerability was related to Apache CXF, a framework for creating flexible, you guessed it, mostly URL-based web-services. The advisory did not directly explain the vector for this exploit, but if anyone knows, I would love to hear how it worked. (Please add a comment!)
The first thing we will do is ensure that all traffic to our website is blocked by Rewrite. This is done by adding a very simple rule, which intercepts traffic in the inbound (requested) direction, and asserts that the request must have come from an internal source (via a Servlet Forward,) otherwise the request will be blocked by returning the
Simple enough. Now that we’ve blocked all external traffic to our site, we need to start allowing legitimate traffic back in; otherwise, our site will not be of much use to anyone.
We do this by creating a rule that inspects the inbound URLs and checks to make sure that only pre-approved characters are used – meaning we know what a secure URL should look like, and if it contains characters we’ve dubbed as “not secure”, then we aren’t going to accept it.
You’ll need to choose acceptable characters to meet your website’s requirements, but if you are not doing anything to fancy with your URLs, then we can expect that only a reasonable few special characters will be required.
Congratulations! We’ve now secured our URL paths from many types of malicious attacks including those used against Struts, Spring, and Seam. This is a valiant step forward, and it was as easy as 10-15 lines of code. We are, however, still missing an important part of the story.
So… Coffee’s still hot. What next?
However, in this scenario, it may actually be simpler just to write our own
But there’s more…
You can request a copy of the security research paper from Aspect Security’s website.
Runtime.getRuntime().exec("system command")
, which gives the ability to run any native system command available to the user under which the Java Virtual Machine (JVM) is running; once you are running code on the host system, as the article explains, the potential threat exists of losing control of the server entirely. The first example is Struts 2 – still a popular framework in many places.
I found it rather interesting that two of the top three recent security exploits used web-application URLs as a vector of attack, and this came at a timely moment for me, the day I was to present “Security and Usability: URL-rewriting for the next-generation web-user” at the Philadelphia Java User’s Group, a talk in which I discuss the vulnerabilities of URLs, and the risks of improper (or missing) URL validation. I quickly jumped to incorporate relevant pieces of information in my talk.
How can we protect ourselves?
This leads to the obvious question: if the frameworks we are using may have inherent insecurities, how can we patch the holes and lock down a potentially vulnerable application? Also, how can we do this without modifying potentially sensitive application code or business logic? The answer is simple – we can apply validation at the URL level, either using Apache (or another Proxy-based solution,) or using a URLRewriteFilter such as [[Rewrite]], an open-source solution from OCPsoft. The principle is simple. If we scrub as many possible URLs in our application for characters that we are certain should never be used, then we can effectively reduce the risk that someone might be able to inject and execute code in our application. URLs are not the only vectors for attack, so we’ll also take a look at how to secure additional forms of user input.Secure the URL
For the following examples, we will assume that we already have the Rewrite framework installed in our application, but if you need to install it, it’s as simple as adding one JAR file to your project: Install Rewrite. Once this is done, we will start to craft our Security rules.You can also use a URL-rewriting framework of your choice, but you will need to “convert” this configuration to your framework’s configuration.
HTTP 404 "Not found"
error code. We also set a low-enough priority so we can be certain that no other providers will execute first.
SecurityRewriteConfiguration.java
public class SecurityRewriteConfiguration extends HttpConfigurationProvider { @Override public Configuration getConfiguration(ServletContext context) { return ConfigurationBuilder.begin() /* * Block any in-bound request not forwarded * from within the appication itself. */ .defineRule() .when(Direction.isInbound().andNot(DispatchType.isForward())) .perform(SendStatus.error(404)); } @Override public int priority() { /* * Providers with lower priority are executed first. */ return -1000; } }
SecurityRewriteConfiguration.java
@Override public Configuration getConfiguration(ServletContext context) { Constraint<String> selectedCharacters = new Constraint<String>() { @Override public boolean isSatisfiedBy(Rewrite event, EvaluationContext context, String value) { return value.matches("[a-zA-Z0-9/:&?.-=_+]*"); } }; return ConfigurationBuilder.begin() .defineRule() .when(Direction.isInbound() .andNot(DispatchType.isForward()) .and(Path.captureIn("path")) .and(URL.captureIn("url") .where("url").constrainedBy(selectedCharacters))) ) .perform(Forward.to("{path}")) /* * Block any in-bound request not forwarded by our previous rule. */ .defineRule() .when(Direction.isInbound().andNot(DispatchType.isForward())) .perform(SendStatus.error(404)); }
Secure all HTTP parameters
The URL of our application may now be much more secure for GET requests and query-parameters, but we haven’t done anything about our HTTP POST parameters. Without securing these, any inputs from HTML<form>
will be vulnerable (or if people just start sending us random requests,) and it’s easy to add this support using another few conditional statements in our configuration.
We need only use the RequestParameter
condition to inspect the value of any provided HTTP parameters (including query-string GET and form POST parameters.) Notice that we also use .orNot(RequestParameter.exists("{}"))
, which ensures that having no parameters is still an acceptable scenario.
Add validation of HTTP parameters
.defineRule() .when(Direction.isInbound() .andNot(DispatchType.isForward()) .and(Path.captureIn("path")) .and(URL.captureIn("url") .where("url").constrainedBy(selectedCharacters)) .and(RequestParameter.matchesAll("{name}", "{value}") .where("name").constrainedBy(selectedCharacters) .where("value").constrainedBy(selectedCharacters) .orNot(RequestParameter.exists("{}"))) ) .perform(Forward.to("{path}"))
Customize the rules
While this global rule may work for a large number of cases in our application, it’s worth noting that you need not open up all URLs using a single rule. We could just as easily have defined custom rules to match certain or specific addresses in our application. For instance, if we want to allow more characters in a password field, we can add an additional rule, which only applies to our Login page, and opens up this functionality.Notice how we have added the
.where("name").matches("(?!password).*")
statement in order to apply our constraints to all fields except the password, and we’ve only exposed this on POST requests, via the .and(Method.isPost())
statement.Exposing the login page form submission
.defineRule() .when(Direction.isInbound() .and(Method.isPost()) .andNot(DispatchType.isForward()) .and(Path.matches("/login")) .and(RequestParameter.matchesAll("{name}", "{value}") .where("name").matches("(?!password).*").constrainedBy(selectedCharacters) .where("value").constrainedBy(selectedCharacters) .orNot(RequestParameter.exists("{}"))) ) .perform(Forward.to("/login"))
Condition
to perform the restriction we want. Writing custom conditions is easy, and means that you have full control over the Rule matching algorithm.
Writing a custom Condition
.and(new Condition() { @Override public boolean evaluate(Rewrite event, EvaluationContext context) { // TODO Return true if the condition is satisfied return false; } })
Secure the HTTP headers
If we really want to do our diligence, we also need to secure the HTTP headers as well – this can also be done using a URL-rewriting framework. It will look very similar to the way we secured HTTP request parameters.Securing the HTTP headers
.defineRule() .when(Direction.isInbound() .and(Method.isPost()) .andNot(DispatchType.isForward()) .and(Path.captureIn("path")) .and(Header.matchesAll("{name}", "{value}") .where("name").constrainedBy(selectedCharacters) .where("value").constrainedBy(selectedCharacters) .orNot(Header.exists("{}"))) ) .perform(Forward.to("{path}"))
Conclusion
Enough code, more talk. This proof of concept work shows how it is possible to secure, or at least improve upon the existing security of, existing Java web-applications. It also shows how a few simple rules can be used to facilitate securing many pages, or very specific pages, and the important thing to remember is that no matter what web-framework you are using – you can still add additional validation and security on top using URL-rewriting techniques. Keep in mind that you should implement security at all levels of your application. For securing your business methods, Apache DeltaSpike Security will be very helpful for CDI-based applications, Seam Security and Spring Security also have good solutions. Do not consider this article to be “the answer” for security. It is just one option, and one additional technique you can use to protect your sensitive applications and information. If you would like to learn more about the OCPsoft Rewrite framework, you can visit the project homepage, or check out the sources on GitHub. There are [[Calendar|Regular development meetings]] held every Monday at 2pm EST (GMT+5) on Google Hangout.Posted in OpenSource, Rewrite
Hi,
great post. One more thing to take care of. Even if you application is the securest possible you still would need to do a lot more hardening on different other levels.
E.G. Appserver, Network, DB …
I wrote up a little hardening guide for GlassFish a few months back .. might be useful to anybody interested in security.
http://blog.eisele.net/2011/05/securing-your-glassfish-hardening-guide.html
– M
Thanks Markus, Very nice article!
Hi Lincoln
Very nice article.
I think there is no clarification between Prettyfaces and Rewrite use cases.
Which one is good for which use cases?
I have an enterprise JSF(Primefaces) and CDI based project. Which one should I use.
PrettyFaces 4 will use Rewrite as a core. Currently PrettyFaces 3 does not use Rewrite, but in theory, both can be used together. (I would try not to.)
Depending if you want easy configuration for URL-mappings, injections, and page actions, PrettyFaces 3 may be a better option for you at the moment, but Rewrite provides much more control over the entire lifecycle.
[…] Baxter III asks Is your web application secure? HTTP attacks are real, and dangerous: According to a recent research paper by the Aspect Security Group, entitled The Unfortunate […]
Nice article! A couple of things:
* You’re filtering GET and POST parameter inputs, but what about other vectors? HTTP Headers come to mind.
* The filtering you’re doing does not seem to be canonicalizing the strings, unless I’m missing something. An attacker could just URI-encode the string and circumvent the filtering.
Hi David,
You’re correct about the HTTP headers! I’ve added a short section describing this as well.
Regarding canonicalization – that’s one approach. I don’t think this approach is suceptible to URI encoded attacks, though, because these rules will block on any ‘%’ characters. So URI encoding is disallowed by default, and be met with the 404 page.
~Lincoln
Is your web application secure? HTTP attacks are real, and dangerous…
Thank you for submitting this cool story – Trackback from JavaPins…
Very useful article. Using today PrettyFaces for URL Rewriting.
I was considering using Rewrite for URL validation in order to add security but I am against adding it along with PrettyFaces.
Looking forward to using PrettyFaces 4.
Exellent Job Guys.
[…] Is your web application secure? HTTP attacks are real, and dangerous […]