This article will describe how to protect your J2EE application from Failure to Restrict URL Access attacks using ESAPI and other techniques. As with all of the detail articles in this series, if you need a refresher on OWASP or ESAPI, please see the intro article The OWASP Top Ten and ESAPI.
What does Failure to Restrict URL Access mean? As usual, let’s check in with what OWASP says it is:
“Frequently, the only protection for a URL is that links to that page are not presented to unauthorized users. However, a motivated, skilled, or just plain lucky attacker may be able to find and access these pages, invoke functions, and view data. Security by obscurity is not sufficient to protect sensitive functions and data in an application. Access control checks must be performed before a request to a sensitive function is granted, which ensures that the user is authorized to access that function.” [from here]
Wow, what does all that mean? Well, the Failure to Restrict URL Access (hereafter referred to as URL authz) is simply a failure of performing a subset of authorization. Authorization is a large topic, with many volumes already written on it, but a short overview here will be helpful to the discussion. Authorization is typically described as ensuring a user has access to something, either a business function, data or both. Here’s a simple example:
User Mary makes a request to delete account id 375.
This statement requires authorization verification. Before we look at authorization, it is assumed that the authentication has already been performed, hence Mary is verified to be Mary. Now for the authorization, we must validate that Mary has access to the “delete account” business function and that she can perform that action for account id 375.
There are many, many ways to perform authorization, some better than others. There are 3 basic issues that crop up when dealing with authorization for the developer: security, granularity and maintainability.
The security piece is foundational – is the implementation secure. If a user requests a resource, the authorization should:
1. execute on every configured request (which should be all requests, except possibly static resources available to everyone)
2. allow access if the user has rights to the function and/or data
3. deny access if the user does not have rights to the function and/or data
The granularity piece has to do with flexibility in specifying restrictions about the authorization constraints. Using our example above, the simplest correct authorization would validate 2 things:
1. that Mary has access to the “delete account” function (meaning presumably she could run this function on any data she has access to).
2. that Mary has access to account id 375 (meaning presumably she could run any function she has access to on this data).
A more complex authorization scheme for this same example might provide more granular controls. It may allow Mary to have access to thousands of accounts, and a handful of various functions, but there may be some accounts she has read-only access to, some she can write to, and some she has full access over. Additionally, there may be a limiting feature of time of day, something like Mary is only authorized to perform these functions between 8am – 5pm (normal work hours). Other limits can certainly be thought of as well.
Obviously the second description has more granularity in specifying constraints, but this implementation will also have to be more complex, and more data will have to be captured to provide the matrix of information required to make an authorization decision.
The maintainability piece deals with the complexity of the design, implementation and stored data as mentioned above in the granularity section. The balance between simplicity of implementation and flexibility is an application specific decision. However, some of this complexity can be reduced by using pre-built solutions like ESAPI, as we’ll see shortly.
Ok, now we’ve covered a bit about authorization in general, let’s discuss URL authz in a bit more detail. URL authz is about a user gaining access to a URL that they should / should not have access to. Using the same example above, the request for a business function to delete an account might be a url like:
This is how a request is made in a web app, through URL’s. From this request, we know the user is looking to delete an account from the url, and the account id is 375 from the parameter. In order to validate that this is a valid request, we need to make sure that the user should be able to access the delete account URL. There are a couple of locations this could be done: from the referring page, and from the request handling function itself. You must use the latter, and should use the former for user experience. So, if I’m on the view accounts page, and I do NOT have access to delete, I should not even see the link/button for delete – that’s about user experience. However, if my application prevents the link from displaying, but the user were to directly type it in, he would still have access if I didn’t prevent the access from the request handling function as well. This bit of pseudocode will help get the point across:
JSP code //if user has access to delete show link to delete page //else show nothing ----------------- in the servlet/action handling the delete request //get the user performing the request //get the account id //if the user has access to delete this data proceed //else throw some error, log issue, possibly respond in some way to attempted "hack"
Both of these checks are important, but the second is vital to protecting the application. Doing the first only is a form of security by obscurity, which almost always provides very little protection. It won’t be long until the user guesses that this url exists, or until he sees another user access it, and then is able to get there.
Ok, so we’ve now described how standard authorization and specifically URL authz works, let’s see a few practical tips and tricks about how to implement it.
1. Think about authorization at the beginning of the application architecture and design if possible. This can make things much simpler down the road. You can come up with a defined naming scheme to follow or any other host of ideas to simplify the task at hand.
2. Always authorize URLs at the time of the request processing (for security), and you should authorize access at the time of providing a link (for uxp).
3. Pen-test if possible – use both automated and manual testing. This can help ensure a decent sampling of urls are not accessible. Even if you can’t do it exhaustively, it will help build confidence in your solution if a subset works. If you can exhaustively test – all the better.
4. Remember included files – these can be forgotten sometimes, but these can often be accessed.
5. Put all your UI files under /WEB-INF, and use a solution that displays your pages by accessing the pages and rendering them to the user. There are various ways to accomplish this, including straight servlets as well as many frameworks (struts, spring, JSF, etc). Having your pages under /WEB-INF prevents them from being directly accessible to the end user per the J2EE spec.
6. Don’t rely on the security by obscurity argument that no-one will know your urls – automated tools can find these, and even intelligent testers often can make educated guesses about urls based on other urls … it’s a good bet that /account/delete exists if /account/view exists.
7. Use an application intrusion detection system (like AppSensor) in order to detect and possibly respond to attacks in production where people are doing things like forceful browsing to pages that don’t exist in your system.
8. If possible, use a tool or framework that already provides a solid authorization design to use for your environment so you don’t reinvent the wheel. There are many tools out there that do this for you, but your mileage definitely varies here. I’ll mention 2 that I’ve used: ESAPI and Spring Security. Spring Security works well for many cases since it’s quite simple and has good integration to Spring itself if you’re already using that framework. ESAPI is certainly more flexible and extensive, but you’ll likely have to do a bit more legwork to get the system hooked up to your application. Once done though, it does offer more features.
Here are the methods in the AccessController interface that is part of ESAPI. These are the methods you would use to perform authorization in your system:
public boolean isAuthorized(Object key, Object runtimeParameter); public void assertAuthorized(Object key, Object runtimeParameter) throws AccessControlException; boolean isAuthorizedForURL(String url); boolean isAuthorizedForFunction(String functionName); boolean isAuthorizedForData(String action, Object data); boolean isAuthorizedForFile(String filepath); boolean isAuthorizedForService(String serviceName); void assertAuthorizedForURL(String url) throws AccessControlException; void assertAuthorizedForFunction(String functionName) throws AccessControlException; void assertAuthorizedForData(String action, Object data) throws AccessControlException; void assertAuthorizedForFile(String filepath) throws AccessControlException; void assertAuthorizedForService(String serviceName) throws AccessControlException;
So, in order to accomplish our task of verifying that a user has access to a certain url, you could use the is/assertAuthorizedForURL() methods, or the generic is/assertAuthorized() methods and pass in parameters specifying you want to authorize a URL and the actual URL data. Please note that this may require you write your own AccessController implementation to integrate with your application and configure ESAPI to use it as the AccessController. Alternatively, you could use one of the reference implementations and build your application so that it uses the ESAPI mechanisms to deal with authorization. Either way, ESAPI’s AccessController interface does allow flexibility in performing authorization of methods and data in your application.
So there you have it. URL authz is a subclass of generic authorization in your application. Authorization is an important concept to focus on during the entire development lifecycle, and URL authz is an important implementation detail that you need to get right when building J2EE apps.
Well, that’s it. Hope you’ve found this article helpful. Let me know if you have any questions or suggestions. Feel free to comment.
Other articles in this series:
Part 0: The OWASP Top Ten and ESAPI
Part 1: The OWASP Top Ten and ESAPI – Part 1 – Cross Site Scripting (XSS)
Part 2: The OWASP Top Ten and ESAPI – Part 2 – Injection Flaws
Part 3: The OWASP Top Ten and ESAPI – Part 3 – Malicious File Execution
Part 4: The OWASP Top Ten and ESAPI – Part 4 – Insecure Direct Object Reference
Part 5: The OWASP Top Ten and ESAPI – Part 5 – Cross Site Request Forgery (CSRF)
Part 6: The OWASP Top Ten and ESAPI – Part 6 – Information Leakage and Improper Error Handling
Part 7: The OWASP Top Ten and ESAPI – Part 7 – Broken Authentication and Session Management
Part 8: The OWASP Top Ten and ESAPI – Part 8 – Insecure Cryptographic Storage
Part 9: The OWASP Top Ten and ESAPI – Part 9 – Insecure Communications
Part 10: The OWASP Top Ten and ESAPI – Part 10 – Failure to Restrict URL Access