Designing for Security

by Kevin Garwood

The aim of designing for security is to address the threat of malicious attacks that would damage the RIF system and to prevent sensitive data to be disclosed to unauthorised users. We have designed the RIF to cope with the following threats:

Establishing the expectations of support for security within an end-user environment

In some projects, too much burden of addressing perceived threats is placed on the software rather than the groups that would install it or the end-users who would use it. The cost of having the software cope with a large number of scenarios related to these threats can complicate the code base, increase the software maintenance costs, and take potentially scarce resources away from other aspects of development.

The first line of defence for the threats we have identified is to prescribe how the software should be installed. From a security perspective, the ideal arrangement would be to have the RIF tool suite installed on an isolated private network that was only accessible through a virtual desktop environment. Results of studies would be be accessible through an encrypted USB drive and only machines in a locked secure room would be have available USB ports.

The next best arrangement would be to make the tool suite accessible only within a corporate Intranet. The RIF database would be installed on a server that was isolated within that network. For example, it would only be available on a restricted port.

From a security perspective it would be best if the tools were not visible outside the corporate Intranet. However, we recognise that some projects may want to grant wider access to tools. Although we have designed the RIF so that all its tools can be supported with web-based applications, we have decided that initially, the Data Loader Tool and the IT Governance Tool will be deployed as desktop applications. There are security benefits for using this kind of deployment.

Desktop applications typically require human users to interact with electronic forms in order to access the underlying data storage. In contrast, the requests that support user interaction in web applications can be imitated by malicious automated scripts. Both web-browser applications and malicious scripts submit requests via URLs, which are processed by a web server. When a web server receives a request, it cannot make assumptions about the client that is calling it.

Desktop applications can be made to rely on web servers as well, thereby exposing them to the same vulnerabilities as web applications. However, because both the front end code for the administration tools and the middleware are both written in Java, it means that client applications can call a Java middleware service directly. Whereas web services emphasise interoperability between different technologies, the Java service interfaces stress integration using the same technology. In this development context, integration can provide fewer opportunities for threats than interoperability.

Clearly the Study Submission and Study Result Viewer tools need to be deployed as web applications. In this deployment context, the web server needs to be configured so that it can securely handle URL requests coming from outside the corporate network. The web server should be isolated from the rest of the network as much as possible.

The RIF Tool Suite should be installed within a secure corporate Intranet. The database server for the RIF should be isolated within that network, and its database access ports should be strictly limited.
Security-1 : The RIF Tool Suite should be installed within a secure corporate Intranet. The database server for the RIF should be isolated within that network, and its database access ports should be strictly limited.

In the short term, our decision to initially develop the administration tools as desktop applications should help reduce the vulnerabilities from automated script attacks. However, in the long term, these tools may end up being reworked as web applications. In that case, it does not make sense to rely on the security benefits of any one kind of front-end in the presentation layer. Although we hope they could prevent malicious code attacks, we shouldn't assume they will.

Security-2 : Design middleware methods for each tool as if it will be deployed as a web application. However, in the short term of RIF development, tools that do not require remote access should be developed as desktop applications that use Java-based RIF services. Tools that require remote access should be developed as web applications that communicate with the middleware via web services. The web server should be isolated from the rest of the corporate Intranet.

Addressing threat of malicious code attacks

One of the most significant ways of reducing the risk of security threats is to reduce the attack surface that is provided by the code base. An attack surface is the total number of points where a malicious attacker can access the system. We have reduced the attack surface in three ways:

General Design-4 : Wherever possible, limit the paths of execution that are likely to occur.

After taking these measures, the attack surface of the middleware code base should be limited to the methods published in the service APIs. The next step in securing the code base is to make copies of mutable values that the client applications pass to service methods. This is called the "safe copying" technique, and it is discussed in the Concurrency section.

After we create local copies of the parameter objects, the middleware then refers to the parameter objects it created rather than the objects that were produced by the client. The local copies are insulated from any malicious changes that may occur in the original objects during the course of executing a service method. For example, a malicious script could initially call the method

   getGeoLevelSelectValues(
      User user,
      Geography geography) 
      throws RIFServiceException;
found in the rifServices.businessConceptLayer.RIFStudySubmissionAPI interface. Suppose it passed a parameter value Geography which could pass the tests done in the object's checkSecurityViolations() method. Sometime after these checks were done, the client could have a thread that manages to set one of the Geography fields to an SQL injection attack. A malicious client could use threads to change a valid object into a harmful one. To prevent this, we would make a copy of Geography and refer to that. When the client tried to change values, it would have no effect on the middleware because it wasn't using the client's copy of Geography.
Concurrency-9 : Perform a deep-copy of any mutable object that is passed to a service method by a client application. This action will prevent the client from altering parameter values while they are being processed by the middleware.

The next step is to find out if the userID associated with the method call has been blacklisted for past calls that contained malicious code. This check is done first so that an unauthorised user's attempt to access the RIF database will fail as soon as possible.

Security-3 : Consider a service method which has a User object specified as a parameter. Immediately after the User object has been safe-copied, check whether the userID in the copy has been blacklisted. If executing the service method results in a security exception, blacklist the user.

The next security measure is to check every String field of every parameter object for whether the values resemble known forms of SQL Injection or SQL cross scripting attacks. This check should be done immediately after parameter values are safe copied and checked for empty values.

Security-4 : Business classes should have a method checkSecurityViolations(), which recursively check whether any String fields have any malicious code values. Field values should be compared against a variable collection of regular expression patterns that are commonly associated with code attacks.

The task of checking whether a String value matches a pattern of malicious code should be supported by a single piece of extensible code that can be driven from an externally supplied list of malicious patterns. The checks need to be done at a single point in the code base to ensure that the matching activity can be applied uniformly to the parameters of all middleware methods.

Furthermore, the list of malicious code patterns should not be hard coded. Instead, it should read the patterns from a text file that can be altered as new forms of code attacks emerge. The RIF software should come with a default list of patterns, but it should allow projects to modify it. Some projects may not feel comfortable relying on a set of patterns which is visible in a publicly available open source project.

Security-5 : The class rifServices.util.FieldValidationUtility will be responsible for checking all String field values contain malicious code patterns.

The collections of code patterns used to detect malicious values in String input data should not be hard coded in the application. Instead they should be read from a file once at startup. The file will contain a default list of patterns that can be altered by individual projects.

Security-6 : The class rifServices.util.FieldValidationUtility will obtain its list of malicious code patterns from an editable file MaliciousCodePatterns.txt. Projects may wish to modify this file so they can add additional patterns in response to new threats of code attacks.

After parameter objects have been safely copied and every String input value has been checked for malicious code patterns, we switch focus from detection to prevention of attacks.

The RIF uses rifServices.dataStorageLayer.SQLConnectionManager to manage a pool of database connections. In some software projects, a single pool is created where anonymous Connection objects can be assigned to the next user request that needs one.

In the RIF project, Connection objects are pooled per user to provide more safeguards that would prevent a user from attempting to inappropriately access the database. When the middleware creates a new Connection object, it immediately calls a database procedure rif40_setup. This procedure creates user-specific views and temporary tables. Should a malicious attack be executed using a given connection, the limitations of access for the associated user should help limit the damage.

Security-7 : Support connection pooling on a per-user basis rather than having a pool of anonymous connections that can be shared by different users.
Security-8 : Call a database procedure that helps create user-specific views and temporary views. This action makes it easier to control what users can access and what damage they could do if their identities were used as part of a malicious attack.

Another way of using database connections to prevent malicious attacks is to use read-only connections wherever possible. Each connection object can be configured so that they only support read operations. Most of the service methods used for widely accessible services such as rifServices.businessConceptLayer.RIFStudySubmissionAPI and rifServices.businessConceptLayer.RIFStudyResultRetrievalAPI.

Suppose that a malicious client is calling a read only method such as:


ArrayList getGeoLevelSelectValues(
   User user,
   Geography geography)
   throws RIFServiceException;

Further suppose that a malicious client has caused the middleware to create a query to delete database tables. If the query is executed on a read-only Connection used, an exception will be thrown, indicating that an attempt to do a write operation was being made.

Security-9 : For each user, maintain separate pools for read-only and write-enabled connections. If a middleware task does not require writing data, it should use a read-only connection.

The main way we prevent malicious code attacks is to construct SQL queries using Java's java.sql.PreparedStatement. A PreparedStatement uses bind variables that help guarantee that query parameter values could never be confused with database commands. The use of prepared statements helps guarantee that a malicious command will simply be interpreted as a parameter value.

Security-10 : Execute queries using Java's PreparedStatement class and use bind variables to set query parameters.

Preventing sensitive information from being accessed by unauthorised users

This area of design relates to managing registered users and associating them with roles and privileges. Aspects of authorisation and authentication are complex, are often dependent on network and operating system considerations, and may be difficult to install. For now, the RIF database will manage data about its registered users rather than relying on some external technologies.

Security-11 : The RIF tool suite will manage user identities in the RIF database rather than delegate to services that might already be part of network administration services.

The RIF supports user roles that include: RIF manager, RIF student, RIF user, and RIF no suppression. The database uses these roles to limit access permissions of registered users. For example, only users who have the role of RIF manager will be able to use the Data Loader Tool and the Information Governance Tool. A RIF user will be able to see their own studies but not anyone else's studies.

Security-12 : The middleware will rely on the database's system roles to help limit the access users have to data.

Minimising sensitive information in error messages

Another source of security problems is revealing sensitive information through error messages. For example, suppose someone wanted to identify a userID in the system. If they tried kgarwood with a random password, they could get a message such as "Incorrect password for kgarwood", which would tell someone that kgarwood was probably a legitimate userID. Instead, it is better to show the response: "You entered an incorrect user name or password", which would tell a hacker less useful information.

Security-13 : Error messages resulting from invalid login should not reveal information about whether a userID is valid.

It is not good practice to pass a SQLException back to the client applications. Apart from it not providing much human-readable information to end-users, the exception could also include sensitive information that could help hackers learn more about data sets. We are not concerned with whether the names of tables or table fields defined in the RIF schema are displayed to an end-user. These are a matter of public knowledge through our open sourcing efforts. However, there is still a risk that exceptions could contain sensitive data. Therefore, we adopt the policy that if an SQLException is created, it must be caught and logged before throwing a RIFServiceException that contains an intelligible error message. We will ensure that error messages do not contain any more information than they need.

Security-14 : Whenever a java.sql.SQLException is thrown in the code, it will be caught and logged. A rifServices.system.RIFServiceException will be thrown, and its error messages will not expose sensitive information beyond what is necessary.