Securing a Distributed Business Objects accessed by many client types
such as Rich PowerBuilder Clients and HTML Clients is a bit more complex than securing a
Client Server Business Object. Client Server Business Objects live in a very controlled
world where the integrity of the objects user is known and the object does not have to
worry about unauthorized access, or if it does it is normally only through business rules
in the validation routine. Some examples:
- In an expense report application the user is presented with a list of their expense
reports, from here they can select an expense report to view/edit. So you as a developer
of the Business Object would never have to worry about the user opening a record that did
not belong to them.
- Client Server Business Objects use the already validated connection to the database,
thus triggers can be used to show who updated the record last. Also you can be pretty sure
the user is who the transaction object says they are.
In Distributed and Web application you cannot be sure of anything. You need to add two
new layers of security to the basic security you have already in place.
Authentication
The first level of security is authentication. Authentication is the process of making
sure you know who the person accessing and executing the object really is. The simplest
approach in distributed PowerBuilder is to pass the user ID and password during the
connectionbegin event of the application. Then you could validate the user ID and password
against the database before allowing processing. While this is secure it is too slow for
distributed processing where objects are created on demand.
Another restriction to this problem is if you want to expose your objects through
something other than Distributed PowerBuilder such as COM or CORBA. You could add a method
that passes the ID and password to the object when it is created, then validate the ID and
password against the database, again even with the largest transaction pool in the world
you will still run into speed problems with this approach. You could erase the speed
problem by creating a table of ID's and passwords which you could check, but this is not
very secure and would fail even the most basic security audits.
The best solution is to authenticate the user once right at the start of their session
and create a token or cookie which is sent to the client. When the user wants to create an
object they pass you this cookie to prove they are secure.
The cookie should contain some basic information about the user such as their ID, their
IP address, the date and time the cookie was created and a check sum to make sure the
cookie has not been altered. Then you should encrypt the cookie using an encryption
algorithm. The users password should never be in the cookie.
Then when the client creates an object they can call a method passing the cookie, the
object checks the cookie to make sure it is valid and extracts the user ID from the
cookie. The User ID is then stored in the object. Whenever you perform any processing that
requires the user ID you should always use the user ID from the cookie. Whenever possible
do not create a method that passes the user ID, always make use of the ID from the cookie.
This will help stop unauthorized transactions.
Inside the object create a Boolean flag ib_secure by default make this
flag FALSE. Then when you have validated the cookie set the flag to TRUE. All
public methods in your object should check the flag as the first thing they do, if the
flag is FALSE then the functions should return a bad return code.
The UserID and Secure attributes should be private attributes of your ancestor business
object. Provide a protected method to query the flag and a private method to set the flag.
Your business entity should try to guard these attributes, even from the developers who
will inherit from it.
After the cookie has been checked the setcookie function should call the setup routine
for the object, why waste time setting up the object before we know who the user is? You
can achieve this by creating a setup virtual method in the ancestor which your ancestor
calls. The developer of the business object should override this to perform their setup
work. This also gives you the chance to return a bad return code if something goes wrong
and so is a much better way of setting the object up than using the constructor event. In
fact as a rule of thumb Never place any code in the constructor event of a distributed
object because you have no way of telling the client if something went wrong.
Unauthorized Access
In the client server world you did not have to validate that the user requesting the
object your entity was being asked to retrieve has the rights to retrieve the object. In
the Web and distributed objects world you have to make sure that the user requesting the
record has the rights to view the record.
Imagine a web application where the HREFS in the list of objects contains the ID of the
record to be opened. When the user clicks a HREF to view the record they could alter the
HREF to point to a record that was not on the original list of objects! Even if you remove
the location field from the browser window as we have done then you cannot stop them book
marking the page, opening another browser window and going to the bookmark and then
breaching security.
So your business object must check in any retrieve functions that the user who was
identified to you in the cookie has the rights to view the record they are asking to see.
Other Issues
As well as the user problems, you have to think about securing the objects from fellow
developers and Power Users. Think about people connecting the development objects to the
production database, or some one logging into a development version of the application and
using that cookie to access the production middle tier.
Another layer of security is to make the Business Object aware of the Environment it is
running in (PROD, BETA, TEST, DEVL) and add the environment to the cookie, a PROD middle
tier should only accept cookies from a PROD application.
The code to create the cookies and validate the cookies should not be written in
PowerBuilder. This will help secure the cookies from other development staff, write the
routines in C++ and create a DLL that the PowerBuilder code uses to perform cookie
validation and information extract. Create two DLL's one that can only read cookies, and a
second that can create cookies. Closely guard the cookie creation DLL.
Wrap Up
I hope this has opened your eye's to the potential security problems you will face as
you move your business entities into a three tier model. Try to think of these issues
while you are still in the design phase, its much easier to tackle the problems there than
to patch the problem in testing! |