Hey all,

 

I'm listing just a few security issues and possible solutions below.
For the benefit of all, by all means challenge my assumptions, and add
your own problems.  But please at least consider my Problem 3 listed
below, as I'm very curious as to whether someone's found a better
elegant solution.

 

 

Problem 1:  Protecting pages from being accessed anonymously

 

In other words, unless the user is logged in, they should not have
access to any page other than, most likely, your login-page.

 

A common solution is to create an abstract superclass for your pages
that, in its pageBeginRender listener, redirects to the login page if
there isn't, for example, a User ASO present.

 

 

Problem 2:  Protecting different pages from different logged-in users

 

This is really just a more-specific version of Problem 1, and you could
use a more-specific version of the solution: create an abstract
superclass that subclasses the superclass from Problem 1 that checks to
ensure that the user is a specific user.

 

This can be hard to maintain, though, especially if your various pages
can be accessed by various combinations of user-types.  E.g. you don't
want to have to create superclass protection-page for every combination
of user-types in your system.  Better to, instead, assign a set of
accesses/permissions to your users, and protecting your pages based on
the presence/absence of a specific access, which could be assigned to
multiple user-types.

 

You also might consider working something like Acegi security into your
system, which can protect a method based on whether a given
role/permission exists.  Google revealed at least one project with this
approach: http://www.carmanconsulting.com/tapestry-acegi

 

 

Problem 3:  Protecting the application from logged-in users who are
spoofing form parameters

 

So, you've ensured that the only users accessing your system have valid
accounts, and are only accessing the pages and interfaces you've given
them.

 

Well, what if they're malicious, after all, and know how a Tapestry URL
works?  They see that when selecting an Entity to edit from a list, that
the URLs differ like so: "...sp=1...", "...sp=2...", "...sp=5...", etc.
Correctly guessing that's the ID of the corresponding Entity, the user
spoofs the URL to be "...sp=10...".  Obviously, they should have
permission to edit Entities, since they need to be able to edit their
own Entities, so this page renders correctly.  i.e. even with Problems 1
and 2 solved, there is still the issue of the user being able to modify
data for other users.

 

We could have our listener method first call an appropriate service
method that determines, in the example above, whether the logged-in user
has permission to edit the given entity.

 

However, we don't want to violate the DRY principle: if there are
multiple places where a given Entity can be selected for editing, we
would have to add this check in each place.

 

Is AOP a solution?  Through AOP/Acegi/Spring, you could wrap advice
around your service methods that has access to the
currently-logged-in-user.  This would be perfect if you were only
verifying the type of the user, but in this case we're determining
validity based on the values of the given method-parameters.  Most
service methods will require a different query/method for determining if
the user and the given parameters are a "match", so this would probably
end up requiring a different Aspect for each method, which doesn't
really gain us much.

 

So ... the best I can come up with at the moment is to pass the User
object into the service methods that need to be protected, and doing the
check inside the method.  Persistence layers should perform individual
units of work, relatively agnostic to what environment it exists in.
The service layer, however, is for business logic, and it's certainly
business logic to declare that a user can only edit the Entities that it
owns.  And since the User object should be a POJO that is not Tapestry
specific, this doesn't tie the service layer to a particular
UI-implementation.  Further, you're defining this logic in only one
place, and your compiler will remind you in each corresponding Tapestry
listener that a given service method must be made aware of what user is
calling it.

 

A way to improve on this is probably to inject the user-object into your
service object at the object-level instead of the method-level.  If
you're using HiveMind for your services, this is simple enough.  It's
well-documented how to make Spring services available to
Tapestry/HiveMind - has anyone done the reverse?  i.e. made it possible
to inject Tapestry ASOs into a Spring service?

 

Has anyone tackled this more elegantly than I've described?  Perhaps
with a totally different design approach to security and
user-separation?

 

This problem has other forms ... such as the user tampering with
serialized objects.  This would be an even rarer case than above,
however, and partially mitigated if we've ensured the user can only
screw with his/her own information.  However, one potential problem that
comes to mind with this has to do with transitive persistence (i.e. if
you're using an ORM like Hibernate, entities attached to the entity
you're persisting might also be persisted).  If for some reason you've
mapped your entities so that AddressType is persisted when the Address
it's attached to is persisted, and this is further being serialized in
the HTML, it could be possible for the user to change that AddressType
(which could, of course, be associated with thousands of other
Addresses).  Obviously, that example is an anti-pattern that shouldn't
happen in the first place, but it nonetheless illustrates a couple areas
that deserve some critical thinking.

 

 

Thanks,

Jim

Reply via email to