Building Security into Software with Security Policies & Static Analysis

The common approach to securing applications is to try to identify and remove all of the application's security vulnerabilities at the end of the development process. However, this bug-finding approach is not only resource-intensive, it's largely ineffective. To have any chance of exposing all of the security vulnerabilities that may be nested throughout the application, the team would have to identify every single path through the application then rigorously test each and every one. And any error found would be difficult to fix, considering that the effort, cost, and time required to fix each one increases exponentially as the development process progresses. Most importantly, the bug-finding approach to security fails to address the root cause of the problem - the fact that security, like quality, must be built into the application.

Building security into an application involves designing and implementing the application according to a policy for reducing the risk of security attacks then verifying that the policy is implemented and operating correctly.

In other words, security requirements should be defined, implemented, and verified just like other requirements.

For example, establishing a policy to apply user input validation immediately after the input values are received guarantees that all inputs are cleaned before they're passed down through the infinite paths of the code to wreak havoc. If this requirement is defined in the security policy and then verified as implemented in the code, the team doesn't need to spend countless resources trying to find and test every possibility for user input.

One of the best strategies for building security into the application is to define how code needs to be written to protect it from attacks then use static analysis to verify that the policy is implemented in the code. This article provides an overview of how this can be done.

Develop a Security Policy
Security policies are espoused by security experts such as OWASP and mandated for compliance by many regulations such as Sarbanes-Oxley that require organizations to demonstrate they have done "due diligence" in safeguarding application security and information privacy. Yet, although the term is mentioned frequently, it's not often defined. A security policy is a specification document that defines how code needs to be written to protect it from attacks. Security policies typically include custom security requirements, privacy requirements, security coding best practices, security application design rules, and security testing benchmarks.

What do you do if your team doesn't have a well-defined security policy? If the organization has designated security experts, they should be writing these requirements. If not, security consultants can be brought in to help develop appropriate requirements for the specific application under development. Obviously, this would require considerable interaction with the internal team members most familiar with the application.

The security policy should describe what kinds of resources require privileged access, what kind of actions should be logged, what kind of inputs should be validated, and other security concerns specific to the application. To be sure key requirements aren't overlooked, I recommend listing all the important assets that a given application interacts with then prioritizing them based on the importance of protecting each asset.

Verify that the Security Policy is Implemented in the Code
Having an effective security policy defined on paper won't translate into a secure application unless the developers follow it in writing their code. Static analysis can be used to automatically verify whether most security policy requirements are actually implemented in the code and identify code that still needs work, isolating the remaining security policy requirements that might require unit testing, component testing, peer code review, or other techniques.

Using static analysis to automatically verify the code's compliance to application-specific security policy requirements (for instance, for authentication, authorization, logging, and input validation) requires expressing those requirements as custom static analysis rules then configuring the tool to check those custom rules. Often, developing such custom rules is simply a matter of tailoring the static analysis tool's available security policy rule templates to suit your own policy. For instance, custom SOA security policy rules can be created from templates such as:

Static analysis can also be used to check whether code complies with industry-standard security best practices developed for the applicable language and technologies. Many available static analysis tools can check compliance to such standards out-of-the-box with no special configuration.

If you're developing in Java, you'd want to do static analysis to check industry-standard Java security rules such as:

For an example of how following such industry-standard rules can prevent security vulnerabilities, consider the rule "Validate an HttpServletRequest object when extracting data from it." Following this rule is important because HttpServletRequest objects contain user-modifiable data that, if left unvalidated and passed to sensitive methods, could allow serious security attacks such as SQL injection and cross-site scripting. Static analysis would report a violation of this rule for the code below because it allows unvalidated user data to be passed on to sensitive methods:

String name = req.getParameter("name");

To comply with this rule, the code would have to be modified as follows:

try {
    String name = ISOValidator.validate(req.getParameter("name"));
} catch (ISOValidationException e) {
    ISOStandardLogger.log(e);
}


For SOA applications, applying industry-standard static analysis rules can expose common security vulnerabilities that manifest themselves in XML. For example, static analysis could be used to parse DTDs and check for recursive entity declarations that, when parsed, can quickly explode exponentially to a large number of XML elements. If such "XML bombs" are left undetected, they can consume the XML parser and cause a denial of service attack. For instance, static analysis could be used to identify the following DTD that, when processed, explodes to a series of 2100 "Bang!" elements and will cause a denial of service:

<?xml version="1.0" ?>
<!DOCTYPE foobar [
    <!ENTITY x0 "Bang!">
    <!ENTITY x1 "&x0;&x0;">
    <!ENTITY x2 "&x1;&x1;">
    ...
    <!ENTITY x99 "&x98;&x98;">
    <!ENTITY x100 "&x99;&x99;">
]>

Take Additional Measures to Safeguard Security
After you're confident that the security policy is implemented in the code, a smoke test can help you verify that the security mechanisms operate correctly. This is done by penetration testing, which involves manually or automatically trying to mimic an attacker's actions and checking if any tested scenarios result in security breaches. When penetration testing is done this way, it can provide reasonable assurance of the application's security after it has verified just several paths through each security-related function.

If the security policy was enforced using static analysis, the penetration testing should reveal two things:
1.  Problems related to security policy requirements that can't be enforced through static analysis (for instance, requirements involving Perl): If problems are identified, either the security policy must be refined or the code isn't functioning correctly and has to be corrected. In the latter case, locating the source of the problem will be simplified if the code's security operations are centralized (as required by the recommended security policy).
2.  Missing requirements: For example, consider a Web application that requires users to register. The registration form takes in a variety of fields; one of them is e-mail. The e-mail field is known to take any input. In this case, the application is missing a requirement to verify that a valid e-mail is input into the field. Moreover, to ensure that code remains secure as the application evolves, all security-related tests (including penetration tests, static analysis tests, and other security requirements tests) should be added to a regression test suite, and this test suite should be run on a regularly scheduled basis (preferably nightly). Tests are then run consistently, without disrupting the existing development process. If no problems are identified, no team intervention is required. If tests find that code modifications reintroduce previously corrected security vulnerabilities or introduce new ones, the team is alerted immediately. This automated testing ensures that applications remain secure over time and also provides documented proof that the application security policy has been enforced.

Implement a Process for Updating the Policy
Because a security policy is a living, breathing document, it needs to be updated regularly. Figure 1 depicts a recommended process for updating this policy.

As new vulnerabilities are found, you isolate them and find the root cause of the issue. Once the root cause is identified, a policy is implemented around it. A fix for the vulnerability is determined, and then - if feasible - your static analysis tool is configured to check whether code is written according to the new rule. This checking is then added to your regularly scheduled regression tests so that, moving forward, you know that the vulnerability remains fixed. The policy is then applied across the application and organization ensuring that every instance of that vulnerability is fixed.

If this process seems familiar, it's because its roots trace back to W. Edwards Deming's TQM (Total Quality Management) and Parasoft's AEP (Automated Error Prevention). This process is designed to fit seamlessly into your existing quality improvement process, thereby reducing barriers to adoption.

Security = Quality
It used to be that security was perceived as a "nice to have" feature, something to focus on - as time permitted - after implementing and verifying the key product functionality. However, the industry is starting to realize that security is synonymous with quality. Even if an application's core functionality is rigorously verified, security vulnerabilities in the code can open the door to denial-of-service, cross-site scripting, buffer overflows, injection flaws, and other serious security attacks. Such attacks could prevent the application from functioning as expected - and may stop it from functioning altogether until the source of the attack is identified and resolved. Likewise, if you spend countless resources defining, applying, and validating a security policy but fail to address general reliability and functionality flaws in the application then you can't rest assured that the security safeguards built into your application will operate correctly.

In today's world, an application must be secure to be considered "high-quality" and security can't truly be achieved unless it's implemented as a core part of a comprehensive quality strategy for preventing and detecting errors from the earliest phase of development. Static analysis is a critical tool in implementing such a comprehensive strategy team-wide, with minimal disruption to the team's day-to-day workflow.

© 2008 SYS-CON Media