5 Key Design Decisions That Affect Security in Web Applications

Senior developers and architects often make decisions related to application performance or other areas that have significant ramifications on the security of the application for years to come. Some decisions are obvious: How do we authenticate users? How do we restrict page access to authorized users? Others, however, are not so obvious. The following list comes from empirical data after seeing the architecture of many enterprise applications and the downstream effects of security.

1. Session Replication

Load balancing is a must have for applications with a large user base. While serving static content in this way is relatively easy, challenges start to arise when your application maintains state information across multiple requests. There are many ways to tackle session replication– here are some of the most common:

  • Allow the client to maintain state so that servers don’t have to
  • Persist state data in the database rather than in server memory
  • Use application server’s built-in session replication technology
  • Use third party products, such as Terra-Cotta
  • Tying each session to a particular server by modifying the session cookie

Out of these, maintaining state on the client is often the easiest to implement. Unfortunately, this single decision is often one the most serious you can make for the security of any client-server application. The reason is that clients can modify any data that they send to you. Inevitably, some of the state data shouldn’t be modifiable by an end user such as a price for a product, user permissions, etc. Without sufficient safeguards, client-side state can leave your application open to parameter manipulation at every transaction. Luckily, some frameworks provide protection in the form of client-side state encryption; however, as we’ve seen with the recent Oracle Padding attacks, this method isn’t always foolproof and can leave you with a false sense of security. Another technique involves hashing and signing read-only state data (i.e. the parameters that the client shouldn’t modify), however trying to decide which parameters should be modifiable and which ones shouldn’t can be particularly time consuming — often to the point that developers just ignore it altogether when deadlines become pressing. If you have the choice, elect to maintain state on the server and use one of the many techniques at your disposal to handle session replication.

2. Authorization Context

Many senior developers and architects we’ve spoken to understand that authorization is a challenging topic. Enterprise applications often perform a basic level of authorization: ensuring that the user has sufficient access rights to view a certain page. The problem is that authorization is a multi-layer, domain-specific problem that you can’t easily delegate to the application server or access management tools. For example, an accounting application user has access to the “accounts payable” module but there’s no server-side check to see which accounts the user should be able to issue payments for. Often the code that has sufficient context to see a list of available accounts is so deep in the call stack that it doesn’t have any information about the end user. The workarounds are often ugly: for example, tightly coupling presentation & business logic such that the application checks the list of accounts in a view page where it does have context information about the end user.

A more elegant solution is to anticipate the need for authorization far into the call stack and design appropriately. In some cases this means explicitly passing user context several layers deeper than you normally would; other approaches include having some type of session / thread-specific lookup mechanism that allows any code to access session-related data. The key is to think about this problem upfront so that you don’t waste unnecessary time down the road trying to hack together a solution. See our pattern-level security analysis of Application Controller for more details on this idea.

3. Tags vs. Code in Views

Over the years, most web application development frameworks have made it practical to code entire views/server-pages completely with tags. Dot Net’s ASPX or Java’s JSF pages are examples of this. Building exclusively with tags can sometimes be frustrating when you need to quickly add functionality inside of a view and you don’t have a ready-made tag for that function at your disposal. Some architects and lead developers impose a strict decision that all views must be composed entirely of tags; other architects and lead developers are more liberal in their approach. Inevitably the applications that allow developers to write in-line coding (e.g. PHP, classic ASP, or Scriptlets in Java) have an incredibly tough time eradicating Cross Site Scripting. Rather than augmenting tags with output encoding, developers need to manually escape every form of output in every view. A single decision can lead to tedious, error-prone work for years to come. If you do elect to offer the flexibility of one-off coding, make sure you use static analysis tools to find potential exposures as early as possible.

4. Choice of Development Framework

Call us biased, but we really believe that the framework you choose will dramatically affect the speed at which you can prevent and remediate security vulnerabilities. Building anti-CSRF controls in Django is a matter of turning on adding “@csrf_protect” to your view method. In most Java frameworks you need to build your own solution or use a third party library such as OWASP’s CSRFguard. Generally speaking, the more security features built into the framework the less time you have to spend adding these features into your own code or trying to integrate third party components. Choosing a development framework that takes security seriously will lead to savings down the road. The Secure Web Application Framework Manifesto is an OWASP project designed to help you make that decision.

5. Logging and Monitoring Approach

Most web applications implement some level of diagnostic logging. From a design perspective, however, it is important to leverage logging as a measure of self defense rather than purely from a debugging standpoint. The ability to detect failures and retrace steps can go a long way towards first spotting and then diagnosing a breach. We’ve found that security-specific application logging is not standardized and, as a result, any security-relevant application logging tends to be done inconsistently. When designing your logging strategy, we highly recommend differentiating security events from debugging or standard error events to expedite the investigative process in the event of compromise. We also recommend using standard error codes for security events in order to facilitate monitoring. OWASP’s ESAPI logging allows for event types to distinguish security events from regular logging events. The AppSensor project allows you to implement intrusion detection and automated responses into your application.