The requirement to allow a given to user to see only a subset of the data in
a particular table is a relatively common one. For instance, in a sales
reporting application, you may have a table of total monthly sales for a
number of territories and a given user may only be allowed to view/edit one
territory or several but not all of the territory data. There may also be
users who aren't allowed to see any sales data at all.

For instance, here is some sample data from our fictitious Sales_Report
table:

id, territory_id, date, total_sales
1, 1024, May-2003, $1000
2, 1024, Jun-2003, $1200
3, 1908, May-2003, $800
4, 2100, May-2003, $1300

To handle these requirements, you could use a combination of role-based and
programmatic security. You can use the role-based part to block access to
the sales reporting actions for users that aren't in sales. In other words,
a user would have to have the 'sales' role to view them. That takes care of
the users who can't see any sales data.

Then you need to restrict users who do have the 'sales' role from seeing
data from territories they don't have access to. Consider these three
actions:

/viewSalesReportItem.do?sales_report_id=3
/viewSalesReport.do?territory_id=1024
/viewAllMySalesReports.do

For the first one, you can load the sales_report from the db and then get
the territory_id from it. Don't trust a territory_id coming in from the
request (e.g.
/viewSalesReportItem.do?sales_report_id=1908&territory_id=1024) because the
sales_report_id is really what determines what your app should show, and you
can see in my example URL that a user could just provide a territory_id that
they do have access to, but that isn't the right one for the sales_report
item they are requesting. That may seem obvious, but it is a common mistake
on update actions where the territory_id might be in the request as a POST
parameter in a hidden field. Users can fake POST parameters, too. Once you
have a trustworthy territory_id, check that the user has access to that
territory. There should be some join table in the db that indicates what
territories the current user has access to. If they don't have access, you
want to show some kind of "access denied" page. If it is okay for the access
denied page to be somewhat generic (and you aren't going to show the user
any links that would get them there, so I think this is okay), you can setup
a global forward to the access_denied page and have your Action return that.

The second one is even easier, because it is the territory_id that directly
drives what the app is going to show. Again, just check that the user has
access to the requested territory, and return the "access_denied" global
forward if the check fails. On one app that I worked on, we put a
convenience method in our application's ActionBase class, which all of our
Actions used as their base class. It is a little clunky still, but our
convenience method would return the "access_denied" ActionForward if the
user did not have access, and null if they did have access. The code in each
action that used it looks something like this:

ActionForward accessDenied = checkAccess(mapping, territoryId, request);
if (accessDenied != null) return accessDenied;

We pass the mapping in so that the checkAccess method could get the
"access_denied" global forward. We pass the request in for two reasons. The
first is that the method needs to call request.getRemoteUser() to figure out
who the current user is. The second reason is that we have a utility class
that will cache the list of territories the user has access to in the
session, and it can get to the session from the request.

The third action I listed up there (/viewAllMySalesReports.do) should show a
list of all sales_report rows that the user who requests it has access to.
In this case, you will want to adjust the db query to only return data that
the user can see. You could do that with a WHERE clause like "territory_id
IN (1024, 1908)" or perhaps better yet just join to the table in the db that
says what user has access to which territories. By limiting the results in
the db query, you avoid getting more data than you need and having to weed
through and discard the ones the user isn't supposed to see.


I have kicked around another idea where the programmatic security code would
not be in the action, but rather you would write a security module for a
given request and "plug it in" somewhere. For instance, for the
/viewSalesReportItem.do?sales_report_id=3 request, you would write a module
that would load the sales_report to get the territory_id and then determine
if the user has access to that territory. I was thinking of incorporating an
interface like this in SecurityFilter, where you might write a security
module that would allow or deny access and then register it with the filter
to be used for request URLs that match "/viewSalesReportItem.do". A
mechanism that allowed you to register a security module with a particular
(or a set of) Struts modules is also possible. The system would allow you to
register different modules with different actions, etc. The possible
advantages to using this kind of approach are that security code is
separated from the action itself (divide and conquer at a conceptual and/or
work assignment level) and that you could likely reuse a given module across
multiple actions (like the view and update actions for a sales_report, for
instance).

However, there is a downside that I don't have a good solution for. Notice
how in this example that we had to load a sales_report from the db in the
security module to get the territory_id so that we could decide if the user
has access or not. In this case the action is going to load the sales_report
from the db again, which means we have more db calls than should really be
needed to process this request. One option to avoid the extra call would be
to have the security module save the sales_report object as a request
attribute for the action to retrieve later, but that couples the sales
module and the action too much, in my opinion. Another solution would be to
have some kind of generic, request-scoped cache of any db objects that have
been loaded. That would eliminate the coupling between the security module
and the action, since the action would just say "I want sales_report with
id=3" and it would either be retrieved from the cache or loaded from the db.
The action does not have to know or care if the object had already been
loaded from the db by the security module. But for that to work, you would
need to acquire or implement such a caching system (perhaps there are some
standard solutions, but I haven't researched it much), and then you would
also need to use it whenever you wanted to get something from the db. And
thus, I don't know of any good way to make this kind of thing work yet.

You could use inheritance among your actions to get some of this benefit,
but that is less flexible than the "pluggable" architecture I was proposing
above. For instance, you could have an inheritance structure like this for
your actions:

Action (Struts Action class)
^
|
MyAppBaseAction (base class for all of the Actions in your app)
^
|
SalesReportItemAction (base class that does security for SalesReportItem
actions)
^                   ^
|                   |
ViewSalesReportItem UpdateSalesReportItem (the actions that handle requests)

But that isn't very flexible, and may break down in practice. It would be
easy to inherit from the wrong action, perhaps run into situations where you
would want to inherit from multiple base actions (can't do that), override
the wrong method or forget to call a method in the superclass or your
action, or break the security on an action like ViewSalesReportItem because
you made changes to SalesReportItemAction while implementing some
requirement for UpdateSalesReportItem. Perhaps all of that is manageable
after all, but my current thoughts are that action inheritance like this
could be trouble and should thus be avoided for our security checking
purposes.


And back to your original proposal... It is fairly common for someone to
email a URL to some page in an app to someone else (by chance or by request
from a user who can't see a page that someone else can). If it is just the
request URL+parameters that are encyrpted into the key, anyone with that URL
can get to the page if you don't have any other security checks. You could
encypt the URL+parameters+username to make the key unique to each user. But
that's a lot of stuff to encypt into a query string parameter. But even then
anyone who figures out how to encode their own params would have free reign
over the system. I think it is best to do active checking to avoid creating
any loop-holes that could be exploited to run wild on the system. With
active checking, you might accidentally create a few holes here and there,
but there is no opportunity for someone to make a "master key" like there
would be with the encryption scheme. If you ever had to change the
encryption key, it would invalidate any bookmarks that your users may have
made to pages in your application, too, which may upset them.

-Max

----- Original Message ----- 
From: "Ross Sargant" <[EMAIL PROTECTED]>
To: "Struts Users Mailing List" <[EMAIL PROTECTED]>
Sent: Monday, September 01, 2003 2:59 PM
Subject: Verifying integrity of URLs


Hi,
    This may be a little off topic, but I thought I'd ask what the experts
thought.
Many actions often take URL parameters. For example a catalog system's
"viewItem" action might include an id number for the
item you wish to view. The action uses this ID to pull something out of your
RDBMS (ideally through a service interface), sets a request attribute and
then forwards to a JSP to show the relevant data.

Now imagine the case where there are complex rules governing who should be
able to view what items. Say for example that a certain user can only view
one category of items (which would obviously be enforced by the search
interface-you would never generate a link to something they weren't supposed
to see in your search results).

What is the best way in general to ensure that the user is not playing
around with url parameters (in this example by manipulating the id
parameter) to get at things they shouldn't. Assume for the moment that the
RDBMS cannot help you solve the problem.I don't really see how Roles help
here because my understanding of Roles is that they only enable and restrict
actions.

One option is to include the security validation in the ViewItem action
itself so it will not just blindly display whatever object the parameters
referred to.This seems like a problem that might occur in numerous different
places though which means it might be benificial to have something more
generic.

My other thought was to append a URL parameter that is basically a digital
signature of the entire URL+query string (before appending the signature).
Assuming for now that the private key is safe on the server, then this would
provide a generic way to prove that an incoming request came from a link
generated by the site. This approach takes the view that if the user is not
meant to see or do something, they are never presented a link in the first
place to access it and it is impossible for them to fake a get request that
works without knowing the private key. This might introduce peformance
concerns.Note that I'm not even talking about public/private key crypto
here.. just simple single private key encrypt/decrypt. This would really
work for any combination of action+parameters you were concerned about.

I was wondering if anyone out there had any better ideas or advice on how
they handle this type of problem in an MVC style application while keeping
things fairly simple.

Ross













---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to